diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2008-03-18 22:04:14 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2008-03-18 22:04:14 +0000 |
commit | 0d49838df6f54c5b49d40a8cf396ea799bd3c4d9 (patch) | |
tree | 10bc9771479d02eb22a9a2fcec20e758ad5715bb /src/backend/executor/functions.c | |
parent | 433c5238bf1980b917b758f3003f16f134c34540 (diff) | |
download | postgresql-0d49838df6f54c5b49d40a8cf396ea799bd3c4d9.tar.gz postgresql-0d49838df6f54c5b49d40a8cf396ea799bd3c4d9.zip |
Arrange to "inline" SQL functions that appear in a query's FROM clause,
are declared to return set, and consist of just a single SELECT. We
can replace the FROM-item with a sub-SELECT and then optimize much as
if we were dealing with a view. Patch from Richard Rowell, cleaned up
by me.
Diffstat (limited to 'src/backend/executor/functions.c')
-rw-r--r-- | src/backend/executor/functions.c | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 0dec881d37e..da6976b62f7 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.120 2008/01/01 19:45:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.121 2008/03/18 22:04:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "commands/trigger.h" #include "executor/functions.h" #include "funcapi.h" +#include "nodes/makefuncs.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "tcop/tcopprot.h" @@ -269,6 +270,7 @@ init_sql_fcache(FmgrInfo *finfo) fcache->returnsTuple = check_sql_fn_retval(foid, rettype, queryTree_list, + false, &fcache->junkFilter); /* Finally, plan the queries */ @@ -856,7 +858,9 @@ ShutdownSQLFunction(Datum arg) * * The return value of a sql function is the value returned by * the final query in the function. We do some ad-hoc type checking here - * to be sure that the user is returning the type he claims. + * to be sure that the user is returning the type he claims. There are + * also a couple of strange-looking features to assist callers in dealing + * with allowed special cases, such as binary-compatible result types. * * For a polymorphic function the passed rettype must be the actual resolved * output type of the function; we should never see a polymorphic pseudotype @@ -868,6 +872,10 @@ ShutdownSQLFunction(Datum arg) * allow "SELECT rowtype_expression", this may be false even when the declared * function return type is a rowtype. * + * If insertRelabels is true, then binary-compatible cases are dealt with + * by actually inserting RelabelType nodes into the final SELECT; obviously + * the caller must pass a parsetree that it's okay to modify in this case. + * * If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined * to convert the function's tuple result to the correct output tuple type. * Whenever the result value is false (ie, the function isn't returning a @@ -875,6 +883,7 @@ ShutdownSQLFunction(Datum arg) */ bool check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + bool insertRelabels, JunkFilter **junkFilter) { Query *parse; @@ -945,10 +954,12 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, rettype == VOIDOID) { /* - * For scalar-type returns, the target list should have exactly one - * entry, and its type should agree with what the user declared. (As - * of Postgres 7.2, we accept binary-compatible types too.) + * For scalar-type returns, the target list must have exactly one + * non-junk entry, and its type must agree with what the user + * declared; except we allow binary-compatible types too. */ + TargetEntry *tle; + if (tlistlen != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -956,7 +967,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, format_type_be(rettype)), errdetail("Final SELECT must return exactly one column."))); - restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr); + /* We assume here that non-junk TLEs must come first in tlists */ + tle = (TargetEntry *) linitial(tlist); + Assert(!tle->resjunk); + + restype = exprType((Node *) tle->expr); if (!IsBinaryCoercible(restype, rettype)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -964,6 +979,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, format_type_be(rettype)), errdetail("Actual return type is %s.", format_type_be(restype)))); + if (insertRelabels && restype != rettype) + tle->expr = (Expr *) makeRelabelType(tle->expr, + rettype, + -1, + COERCE_DONTCARE); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { @@ -977,14 +997,24 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, * If the target list is of length 1, and the type of the varnode in * the target list matches the declared return type, this is okay. * This can happen, for example, where the body of the function is - * 'SELECT func2()', where func2 has the same return type as the - * function that's calling it. + * 'SELECT func2()', where func2 has the same composite return type + * as the function that's calling it. */ if (tlistlen == 1) { - restype = exprType((Node *) ((TargetEntry *) linitial(tlist))->expr); + TargetEntry *tle = (TargetEntry *) linitial(tlist); + + Assert(!tle->resjunk); + restype = exprType((Node *) tle->expr); if (IsBinaryCoercible(restype, rettype)) + { + if (insertRelabels && restype != rettype) + tle->expr = (Expr *) makeRelabelType(tle->expr, + rettype, + -1, + COERCE_DONTCARE); return false; /* NOT returning whole tuple */ + } } /* Is the rowtype fixed, or determined only at runtime? */ @@ -1043,6 +1073,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, format_type_be(tletype), format_type_be(atttype), tuplogcols))); + if (insertRelabels && tletype != atttype) + tle->expr = (Expr *) makeRelabelType(tle->expr, + atttype, + -1, + COERCE_DONTCARE); } for (;;) @@ -1070,14 +1105,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* Report that we are returning entire tuple result */ return true; } - else if (IsPolymorphicType(rettype)) - { - /* This should already have been caught ... */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("cannot determine result data type"), - errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); - } else ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |