*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.268 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.269 2008/10/09 19:27:40 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
int nargs, List *args);
static Node *substitute_actual_srf_parameters_mutator(Node *node,
substitute_actual_srf_parameters_context *context);
+static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
/*****************************************************************************
* inline_set_returning_function
* Attempt to "inline" a set-returning function in the FROM clause.
*
- * "node" is the expression from an RTE_FUNCTION rangetable entry. If it
- * represents a call of a set-returning SQL function that can safely be
- * inlined, expand the function and return the substitute Query structure.
- * Otherwise, return NULL.
+ * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
+ * set-returning SQL function that can safely be inlined, expand the function
+ * and return the substitute Query structure. Otherwise, return NULL.
*
* This has a good deal of similarity to inline_function(), but that's
* for the non-set-returning case, and there are enough differences to
* justify separate functions.
*/
Query *
-inline_set_returning_function(PlannerInfo *root, Node *node)
+inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{
FuncExpr *fexpr;
HeapTuple func_tuple;
Query *querytree;
int i;
+ Assert(rte->rtekind == RTE_FUNCTION);
+
/*
* It doesn't make a lot of sense for a SQL SRF to refer to itself
* in its own FROM clause, since that must cause infinite recursion
check_stack_depth();
/* Fail if FROM item isn't a simple FuncExpr */
- if (node == NULL || !IsA(node, FuncExpr))
+ fexpr = (FuncExpr *) rte->funcexpr;
+ if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL;
- fexpr = (FuncExpr *) node;
/*
* The function must be declared to return a set, else inlining would
if (!fexpr->funcretset)
return NULL;
- /* Fail if function returns RECORD ... we don't have enough context */
- if (fexpr->funcresulttype == RECORDOID)
- return NULL;
-
/*
* Refuse to inline if the arguments contain any volatile functions or
* sub-selects. Volatile functions are rejected because inlining may
if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
querytree_list,
true, NULL) &&
- get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE)
+ (get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
+ fexpr->funcresulttype == RECORDOID))
goto fail; /* reject not-whole-tuple-result cases */
+ /*
+ * If it returns RECORD, we have to check against the column type list
+ * provided in the RTE; check_sql_fn_retval can't do that. (If no match,
+ * we just fail to inline, rather than complaining; see notes for
+ * tlist_matches_coltypelist.)
+ */
+ if (fexpr->funcresulttype == RECORDOID &&
+ !tlist_matches_coltypelist(querytree->targetList, rte->funccoltypes))
+ goto fail;
+
/*
* Looks good --- substitute parameters into the query.
*/
substitute_actual_srf_parameters_mutator,
(void *) context);
}
+
+/*
+ * Check whether a SELECT targetlist emits the specified column types,
+ * to see if it's safe to inline a function returning record.
+ *
+ * We insist on exact match here. The executor allows binary-coercible
+ * cases too, but we don't have a way to preserve the correct column types
+ * in the correct places if we inline the function in such a case.
+ *
+ * Note that we only check type OIDs not typmods; this agrees with what the
+ * executor would do at runtime, and attributing a specific typmod to a
+ * function result is largely wishful thinking anyway.
+ */
+static bool
+tlist_matches_coltypelist(List *tlist, List *coltypelist)
+{
+ ListCell *tlistitem;
+ ListCell *clistitem;
+
+ clistitem = list_head(coltypelist);
+ foreach(tlistitem, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
+ Oid coltype;
+
+ if (tle->resjunk)
+ continue; /* ignore junk columns */
+
+ if (clistitem == NULL)
+ return false; /* too many tlist items */
+
+ coltype = lfirst_oid(clistitem);
+ clistitem = lnext(clistitem);
+
+ if (exprType((Node *) tle->expr) != coltype)
+ return false; /* column type mismatch */
+ }
+
+ if (clistitem != NULL)
+ return false; /* too few tlist items */
+
+ return true;
+}