8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.268 2008/10/04 21:56:53 tgl Exp $
11
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.269 2008/10/09 19:27:40 tgl Exp $
12
12
*
13
13
* HISTORY
14
14
* AUTHOR DATE MAJOR EVENT
@@ -111,6 +111,7 @@ static Query *substitute_actual_srf_parameters(Query *expr,
111
111
int nargs , List * args );
112
112
static Node * substitute_actual_srf_parameters_mutator (Node * node ,
113
113
substitute_actual_srf_parameters_context * context );
114
+ static bool tlist_matches_coltypelist (List * tlist , List * coltypelist );
114
115
115
116
116
117
/*****************************************************************************
@@ -3659,17 +3660,16 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod)
3659
3660
* inline_set_returning_function
3660
3661
* Attempt to "inline" a set-returning function in the FROM clause.
3661
3662
*
3662
- * "node" is the expression from an RTE_FUNCTION rangetable entry. If it
3663
- * represents a call of a set-returning SQL function that can safely be
3664
- * inlined, expand the function and return the substitute Query structure.
3665
- * Otherwise, return NULL.
3663
+ * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
3664
+ * set-returning SQL function that can safely be inlined, expand the function
3665
+ * and return the substitute Query structure. Otherwise, return NULL.
3666
3666
*
3667
3667
* This has a good deal of similarity to inline_function(), but that's
3668
3668
* for the non-set-returning case, and there are enough differences to
3669
3669
* justify separate functions.
3670
3670
*/
3671
3671
Query *
3672
- inline_set_returning_function (PlannerInfo * root , Node * node )
3672
+ inline_set_returning_function (PlannerInfo * root , RangeTblEntry * rte )
3673
3673
{
3674
3674
FuncExpr * fexpr ;
3675
3675
HeapTuple func_tuple ;
@@ -3686,6 +3686,8 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
3686
3686
Query * querytree ;
3687
3687
int i ;
3688
3688
3689
+ Assert (rte -> rtekind == RTE_FUNCTION );
3690
+
3689
3691
/*
3690
3692
* It doesn't make a lot of sense for a SQL SRF to refer to itself
3691
3693
* in its own FROM clause, since that must cause infinite recursion
@@ -3695,9 +3697,9 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
3695
3697
check_stack_depth ();
3696
3698
3697
3699
/* Fail if FROM item isn't a simple FuncExpr */
3698
- if (node == NULL || !IsA (node , FuncExpr ))
3700
+ fexpr = (FuncExpr * ) rte -> funcexpr ;
3701
+ if (fexpr == NULL || !IsA (fexpr , FuncExpr ))
3699
3702
return NULL ;
3700
- fexpr = (FuncExpr * ) node ;
3701
3703
3702
3704
/*
3703
3705
* The function must be declared to return a set, else inlining would
@@ -3707,10 +3709,6 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
3707
3709
if (!fexpr -> funcretset )
3708
3710
return NULL ;
3709
3711
3710
- /* Fail if function returns RECORD ... we don't have enough context */
3711
- if (fexpr -> funcresulttype == RECORDOID )
3712
- return NULL ;
3713
-
3714
3712
/*
3715
3713
* Refuse to inline if the arguments contain any volatile functions or
3716
3714
* sub-selects. Volatile functions are rejected because inlining may
@@ -3837,9 +3835,20 @@ inline_set_returning_function(PlannerInfo *root, Node *node)
3837
3835
if (!check_sql_fn_retval (fexpr -> funcid , fexpr -> funcresulttype ,
3838
3836
querytree_list ,
3839
3837
true, NULL ) &&
3840
- get_typtype (fexpr -> funcresulttype ) == TYPTYPE_COMPOSITE )
3838
+ (get_typtype (fexpr -> funcresulttype ) == TYPTYPE_COMPOSITE ||
3839
+ fexpr -> funcresulttype == RECORDOID ))
3841
3840
goto fail ; /* reject not-whole-tuple-result cases */
3842
3841
3842
+ /*
3843
+ * If it returns RECORD, we have to check against the column type list
3844
+ * provided in the RTE; check_sql_fn_retval can't do that. (If no match,
3845
+ * we just fail to inline, rather than complaining; see notes for
3846
+ * tlist_matches_coltypelist.)
3847
+ */
3848
+ if (fexpr -> funcresulttype == RECORDOID &&
3849
+ !tlist_matches_coltypelist (querytree -> targetList , rte -> funccoltypes ))
3850
+ goto fail ;
3851
+
3843
3852
/*
3844
3853
* Looks good --- substitute parameters into the query.
3845
3854
*/
@@ -3938,3 +3947,46 @@ substitute_actual_srf_parameters_mutator(Node *node,
3938
3947
substitute_actual_srf_parameters_mutator ,
3939
3948
(void * ) context );
3940
3949
}
3950
+
3951
+ /*
3952
+ * Check whether a SELECT targetlist emits the specified column types,
3953
+ * to see if it's safe to inline a function returning record.
3954
+ *
3955
+ * We insist on exact match here. The executor allows binary-coercible
3956
+ * cases too, but we don't have a way to preserve the correct column types
3957
+ * in the correct places if we inline the function in such a case.
3958
+ *
3959
+ * Note that we only check type OIDs not typmods; this agrees with what the
3960
+ * executor would do at runtime, and attributing a specific typmod to a
3961
+ * function result is largely wishful thinking anyway.
3962
+ */
3963
+ static bool
3964
+ tlist_matches_coltypelist (List * tlist , List * coltypelist )
3965
+ {
3966
+ ListCell * tlistitem ;
3967
+ ListCell * clistitem ;
3968
+
3969
+ clistitem = list_head (coltypelist );
3970
+ foreach (tlistitem , tlist )
3971
+ {
3972
+ TargetEntry * tle = (TargetEntry * ) lfirst (tlistitem );
3973
+ Oid coltype ;
3974
+
3975
+ if (tle -> resjunk )
3976
+ continue ; /* ignore junk columns */
3977
+
3978
+ if (clistitem == NULL )
3979
+ return false; /* too many tlist items */
3980
+
3981
+ coltype = lfirst_oid (clistitem );
3982
+ clistitem = lnext (clistitem );
3983
+
3984
+ if (exprType ((Node * ) tle -> expr ) != coltype )
3985
+ return false; /* column type mismatch */
3986
+ }
3987
+
3988
+ if (clistitem != NULL )
3989
+ return false; /* too few tlist items */
3990
+
3991
+ return true;
3992
+ }
0 commit comments