@@ -1634,9 +1634,7 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
16341634 * init_fcache is presumed already run on the FuncExprState.
16351635 *
16361636 * This function handles the most general case, wherein the function or
1637- * one of its arguments might (or might not) return a set. If we find
1638- * no sets involved, we will change the FuncExprState's function pointer
1639- * to use a simpler method on subsequent calls.
1637+ * one of its arguments can return a set.
16401638 */
16411639static Datum
16421640ExecMakeFunctionResult (FuncExprState * fcache ,
@@ -1906,13 +1904,12 @@ ExecMakeFunctionResult(FuncExprState *fcache,
19061904 /*
19071905 * Non-set case: much easier.
19081906 *
1909- * We change the ExprState function pointer to use the simpler
1910- * ExecMakeFunctionResultNoSets on subsequent calls. This amounts to
1911- * assuming that no argument can return a set if it didn't do so the
1912- * first time.
1907+ * In common cases, this code path is unreachable because we'd have
1908+ * selected ExecMakeFunctionResultNoSets instead. However, it's
1909+ * possible to get here if an argument sometimes produces set results
1910+ * and sometimes scalar results. For example, a CASE expression might
1911+ * call a set-returning function in only some of its arms.
19131912 */
1914- fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResultNoSets ;
1915-
19161913 if (isDone )
19171914 * isDone = ExprSingleResult ;
19181915
@@ -2371,10 +2368,22 @@ ExecEvalFunc(FuncExprState *fcache,
23712368 init_fcache (func -> funcid , func -> inputcollid , fcache ,
23722369 econtext -> ecxt_per_query_memory , true);
23732370
2374- /* Go directly to ExecMakeFunctionResult on subsequent uses */
2375- fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResult ;
2376-
2377- return ExecMakeFunctionResult (fcache , econtext , isNull , isDone );
2371+ /*
2372+ * We need to invoke ExecMakeFunctionResult if either the function itself
2373+ * or any of its input expressions can return a set. Otherwise, invoke
2374+ * ExecMakeFunctionResultNoSets. In either case, change the evalfunc
2375+ * pointer to go directly there on subsequent uses.
2376+ */
2377+ if (fcache -> func .fn_retset || expression_returns_set ((Node * ) func -> args ))
2378+ {
2379+ fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResult ;
2380+ return ExecMakeFunctionResult (fcache , econtext , isNull , isDone );
2381+ }
2382+ else
2383+ {
2384+ fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResultNoSets ;
2385+ return ExecMakeFunctionResultNoSets (fcache , econtext , isNull , isDone );
2386+ }
23782387}
23792388
23802389/* ----------------------------------------------------------------
@@ -2394,10 +2403,22 @@ ExecEvalOper(FuncExprState *fcache,
23942403 init_fcache (op -> opfuncid , op -> inputcollid , fcache ,
23952404 econtext -> ecxt_per_query_memory , true);
23962405
2397- /* Go directly to ExecMakeFunctionResult on subsequent uses */
2398- fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResult ;
2399-
2400- return ExecMakeFunctionResult (fcache , econtext , isNull , isDone );
2406+ /*
2407+ * We need to invoke ExecMakeFunctionResult if either the function itself
2408+ * or any of its input expressions can return a set. Otherwise, invoke
2409+ * ExecMakeFunctionResultNoSets. In either case, change the evalfunc
2410+ * pointer to go directly there on subsequent uses.
2411+ */
2412+ if (fcache -> func .fn_retset || expression_returns_set ((Node * ) op -> args ))
2413+ {
2414+ fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResult ;
2415+ return ExecMakeFunctionResult (fcache , econtext , isNull , isDone );
2416+ }
2417+ else
2418+ {
2419+ fcache -> xprstate .evalfunc = (ExprStateEvalFunc ) ExecMakeFunctionResultNoSets ;
2420+ return ExecMakeFunctionResultNoSets (fcache , econtext , isNull , isDone );
2421+ }
24012422}
24022423
24032424/* ----------------------------------------------------------------
0 commit comments