Skip to content

Commit f03dcc1

Browse files
author
Commitfest Bot
committed
[CF 5083] v3 - Inline non-SQL SRFs
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5083 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CA+renyXxanvX8rnrqTNkZt1r9=rm59MHSG4DDVfsV2aUGe3dUQ@mail.gmail.com Author(s): Paul Jungwirth
2 parents f6a4c49 + b1160b1 commit f03dcc1

File tree

7 files changed

+755
-115
lines changed

7 files changed

+755
-115
lines changed

doc/src/sgml/xfunc.sgml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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(&amp;sql);
4217+
appendStringInfo(&amp;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

Comments
 (0)