@@ -4159,6 +4159,107 @@ supportfn(internal) returns internal
41594159 expression and an actual execution of the target function.
41604160 </para>
41614161
4162+ <para>
4163+ Similarly, a <link linkend="queries-tablefunctions">set-returning function</link>
4164+ can implement <literal>SupportRequestInlineSRF</literal> to return a
4165+ <literal>Query</literal> node, which the planner will try to inline into
4166+ the outer query, just as <productname>PostgreSQL</productname> inlines
4167+ SQL functions. Normallly only SQL functions can be inlined, but this support
4168+ request allows a function in <link linkend="plpgsql">PL/pgSQL</link>
4169+ or another language to build a dynamic SQL query and have it inlined too.
4170+ The <literal>Query</literal> node must be a <literal>SELECT</literal> query
4171+ that has gone through parse analysis and rewriting.
4172+ You may include <literal>Param</literal> nodes referencing the original function's
4173+ parameters, and <productname>PostgreSQL</productname> will map those appropriately
4174+ to the arguments passed by the caller.
4175+ It is the responsibility of the support function to return
4176+ a node that matches the parent function's implementation.
4177+ We make no guarantee that <productname>PostgreSQL</productname> will
4178+ never call the target function in cases that the support function could
4179+ simplify. Functions called in <literal>SELECT</literal> are not simplified.
4180+ Or if the <literal>RangeTblEntry</literal> has more than one
4181+ <literal>RangeTblFunction</literal> (such as when using
4182+ <literal>ROWS FROM</literal>), the function will not be simplified.
4183+ Ensure rigorous equivalence between the simplified expression and an actual
4184+ execution of the target function.
4185+ </para>
4186+
4187+ <para>
4188+ One way to implement a <literal>SupportRequestInlineSRF</literal> support function
4189+ is to build a SQL string then parse it with <literal>pg_parse_query</literal>.
4190+ The outline of such a function might look like this:
4191+ <programlisting>
4192+ PG_FUNCTION_INFO_V1(my_support_function);
4193+ Datum
4194+ my_support_function(PG_FUNCTION_ARGS)
4195+ {
4196+ Node *rawreq = (Node *) PG_GETARG_POINTER(0);
4197+ SupportRequestInlineSRF *req
4198+ RangeTblFunction *rtfunc;
4199+ FuncExpr *expr;
4200+ Query *querytree;
4201+ StringInfoData sql;
4202+ HeapTuple func_tuple;
4203+ SQLFunctionParseInfoPtr pinfo;
4204+ List *raw_parsetree_list;
4205+
4206+ /* Return if it's not a type we handle. */
4207+ if (!IsA(rawreq, SupportRequestInlineSRF))
4208+ PG_RETURN_POINTER(NULL);
4209+
4210+ /* Get things we need off the support request node. */
4211+ req = (SupportRequestInlineSRF *) rawreq;
4212+ rtfunc = req->rtfunc;
4213+ expr = (FuncExpr *) rtfunc->funcexpr;
4214+
4215+ /* Generate the SQL string. */
4216+ initStringInfo(&sql);
4217+ appendStringInfo(&sql, "SELECT ...");
4218+
4219+ /* Build a SQLFunctionParseInfo. */
4220+ func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(expr->funcid));
4221+ if (!HeapTupleIsValid(func_tuple))
4222+ {
4223+ ereport(WARNING, (errmsg("cache lookup failed for function %u", expr->funcid)));
4224+ PG_RETURN_POINTER(NULL);
4225+ }
4226+ pinfo = prepare_sql_fn_parse_info(func_tuple,
4227+ (Node *) expr,
4228+ expr->inputcollid);
4229+ ReleaseSysCache(func_tuple);
4230+
4231+ /* Parse the SQL. */
4232+ raw_parsetree_list = pg_parse_query(sql.data);
4233+ if (list_length(raw_parsetree_list) != 1)
4234+ {
4235+ ereport(WARNING, (errmsg("my_support_func parsed to more than one node")));
4236+ PG_RETURN_POINTER(NULL);
4237+ }
4238+
4239+ /* Analyze the parse tree as if it were a SQL-language body. */
4240+ querytree_list = pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list),
4241+ sql.data,
4242+ (ParserSetupHook) sql_fn_parser_setup,
4243+ pinfo, NULL);
4244+ if (list_length(querytree_list) != 1)
4245+ {
4246+ ereport(WARNING, (errmsg("my_support_func rewrote to more than one node")));
4247+ PG_RETURN_POINTER(NULL);
4248+ }
4249+
4250+ querytree = linitial(querytree_list);
4251+ if (!IsA(querytree, Query))
4252+ {
4253+ ereport(WARNING, (errmsg("my_support_func didn't parse to a Query"),
4254+ errdetail("Got this instead: %s", nodeToString(querytree))));
4255+ PG_RETURN_POINTER(NULL);
4256+ }
4257+
4258+ PG_RETURN_POINTER(querytree);
4259+ }
4260+ </programlisting>
4261+ </para>
4262+
41624263 <para>
41634264 For target functions that return <type>boolean</type>, it is often useful to estimate
41644265 the fraction of rows that will be selected by a <literal>WHERE</literal> clause using that
0 commit comments