Skip to content

Commit 2f48ede

Browse files
committed
Avoid using a cursor in plpgsql's RETURN QUERY statement.
plpgsql has always executed the query given in a RETURN QUERY command by opening it as a cursor and then fetching a few rows at a time, which it turns around and dumps into the function's result tuplestore. The point of this was to keep from blowing out memory with an oversized SPITupleTable result (note that while a tuplestore can spill tuples to disk, SPITupleTable cannot). However, it's rather inefficient, both because of extra data copying and because of executor entry/exit overhead. In recent versions, a new performance problem has emerged: use of a cursor prevents use of a parallel plan for the executed query. We can improve matters by skipping use of a cursor and having the executor push result tuples directly into the function's result tuplestore. However, a moderate amount of new infrastructure is needed to make that idea work: * We can use the existing tstoreReceiver.c DestReceiver code to funnel executor output to the tuplestore, but it has to be extended to support plpgsql's requirement for possibly applying a tuple conversion map. * SPI needs to be extended to allow use of a caller-supplied DestReceiver instead of its usual receiver that puts tuples into a SPITupleTable. Two new API calls are needed to handle both the RETURN QUERY and RETURN QUERY EXECUTE cases. I also felt that I didn't want these new API calls to use the legacy method of specifying query parameter values with "char" null flags (the old ' '/'n' convention); rather they should accept ParamListInfo objects containing the parameter type and value info. This required a bit of additional new infrastructure since we didn't yet have any parse analysis callback that would interpret $N parameter symbols according to type data supplied in a ParamListInfo. There seems to be no harm in letting makeParamList install that callback by default, rather than leaving a new ParamListInfo's parserSetup hook as NULL. (Indeed, as of HEAD, I couldn't find anyplace that was using the parserSetup field at all; plpgsql was using parserSetupArg for its own purposes, but parserSetup seemed to be write-only.) We can actually get plpgsql out of the business of using legacy null flags altogether, and using ParamListInfo instead of its ad-hoc PreparedParamsData structure; but this requires inventing one more SPI API call that can replace SPI_cursor_open_with_args. That seems worth doing, though. SPI_execute_with_args and SPI_cursor_open_with_args are now unused anywhere in the core PG distribution. Perhaps someday we could deprecate/remove them. But cleaning up the crufty bits of the SPI API is a task for a different patch. Per bug #16040 from Jeremy Smith. This is unfortunately too invasive to consider back-patching. Patch by me; thanks to Hamid Akhtar for review. Discussion: https://postgr.es/m/[email protected]
1 parent aaf8c99 commit 2f48ede

File tree

9 files changed

+796
-147
lines changed

9 files changed

+796
-147
lines changed

doc/src/sgml/spi.sgml

+349
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,133 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
785785

786786
<!-- *********************************************** -->
787787

788+
<refentry id="spi-spi-execute-with-receiver">
789+
<indexterm><primary>SPI_execute_with_receiver</primary></indexterm>
790+
791+
<refmeta>
792+
<refentrytitle>SPI_execute_with_receiver</refentrytitle>
793+
<manvolnum>3</manvolnum>
794+
</refmeta>
795+
796+
<refnamediv>
797+
<refname>SPI_execute_with_receiver</refname>
798+
<refpurpose>execute a command with out-of-line parameters</refpurpose>
799+
</refnamediv>
800+
801+
<refsynopsisdiv>
802+
<synopsis>
803+
int SPI_execute_with_receiver(const char *<parameter>command</parameter>,
804+
ParamListInfo <parameter>params</parameter>,
805+
bool <parameter>read_only</parameter>,
806+
long <parameter>count</parameter>,
807+
DestReceiver *<parameter>dest</parameter>)
808+
</synopsis>
809+
</refsynopsisdiv>
810+
811+
<refsect1>
812+
<title>Description</title>
813+
814+
<para>
815+
<function>SPI_execute_with_receiver</function> executes a command that might
816+
include references to externally supplied parameters. The command text
817+
refers to a parameter as <literal>$<replaceable>n</replaceable></literal>,
818+
and the <parameter>params</parameter> object provides values and type
819+
information for each such symbol.
820+
<parameter>read_only</parameter> and <parameter>count</parameter> have
821+
the same interpretation as in <function>SPI_execute</function>.
822+
</para>
823+
824+
<para>
825+
If <parameter>dest</parameter> is not NULL, then result tuples are passed
826+
to that object as they are generated by the executor, instead of being
827+
accumulated in <varname>SPI_tuptable</varname>. Using a
828+
caller-supplied <literal>DestReceiver</literal> object is particularly
829+
helpful for queries that might generate many tuples, since the data can
830+
be processed on-the-fly instead of being accumulated in memory.
831+
</para>
832+
833+
<para>
834+
The <parameter>params</parameter> object should normally mark each
835+
parameter with the <literal>PARAM_FLAG_CONST</literal> flag, since
836+
a one-shot plan is always used for the query.
837+
</para>
838+
</refsect1>
839+
840+
<refsect1>
841+
<title>Arguments</title>
842+
843+
<variablelist>
844+
<varlistentry>
845+
<term><literal>const char * <parameter>command</parameter></literal></term>
846+
<listitem>
847+
<para>
848+
command string
849+
</para>
850+
</listitem>
851+
</varlistentry>
852+
853+
<varlistentry>
854+
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
855+
<listitem>
856+
<para>
857+
data structure containing parameter types and values; NULL if none
858+
</para>
859+
</listitem>
860+
</varlistentry>
861+
862+
<varlistentry>
863+
<term><literal>bool <parameter>read_only</parameter></literal></term>
864+
<listitem>
865+
<para><literal>true</literal> for read-only execution</para>
866+
</listitem>
867+
</varlistentry>
868+
869+
<varlistentry>
870+
<term><literal>long <parameter>count</parameter></literal></term>
871+
<listitem>
872+
<para>
873+
maximum number of rows to return,
874+
or <literal>0</literal> for no limit
875+
</para>
876+
</listitem>
877+
</varlistentry>
878+
879+
<varlistentry>
880+
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
881+
<listitem>
882+
<para>
883+
<literal>DestReceiver</literal> object that will receive any tuples
884+
emitted by the query; if NULL, tuples are returned
885+
in <varname>SPI_tuptable</varname>
886+
</para>
887+
</listitem>
888+
</varlistentry>
889+
</variablelist>
890+
</refsect1>
891+
892+
<refsect1>
893+
<title>Return Value</title>
894+
895+
<para>
896+
The return value is the same as for <function>SPI_execute</function>.
897+
</para>
898+
899+
<para>
900+
When <parameter>dest</parameter> is NULL,
901+
<varname>SPI_processed</varname> and
902+
<varname>SPI_tuptable</varname> are set as in
903+
<function>SPI_execute</function>.
904+
When <parameter>dest</parameter> is not NULL,
905+
<varname>SPI_processed</varname> is set to zero and
906+
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
907+
is required, the caller's <literal>DestReceiver</literal> object must
908+
calculate it.
909+
</para>
910+
</refsect1>
911+
</refentry>
912+
913+
<!-- *********************************************** -->
914+
788915
<refentry id="spi-spi-prepare">
789916
<indexterm><primary>SPI_prepare</primary></indexterm>
790917

@@ -1564,6 +1691,120 @@ int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
15641691

15651692
<!-- *********************************************** -->
15661693

1694+
<refentry id="spi-spi-execute-plan-with-receiver">
1695+
<indexterm><primary>SPI_execute_plan_with_receiver</primary></indexterm>
1696+
1697+
<refmeta>
1698+
<refentrytitle>SPI_execute_plan_with_receiver</refentrytitle>
1699+
<manvolnum>3</manvolnum>
1700+
</refmeta>
1701+
1702+
<refnamediv>
1703+
<refname>SPI_execute_plan_with_receiver</refname>
1704+
<refpurpose>execute a statement prepared by <function>SPI_prepare</function></refpurpose>
1705+
</refnamediv>
1706+
1707+
<refsynopsisdiv>
1708+
<synopsis>
1709+
int SPI_execute_plan_with_receiver(SPIPlanPtr <parameter>plan</parameter>,
1710+
ParamListInfo <parameter>params</parameter>,
1711+
bool <parameter>read_only</parameter>,
1712+
long <parameter>count</parameter>,
1713+
DestReceiver *<parameter>dest</parameter>)
1714+
</synopsis>
1715+
</refsynopsisdiv>
1716+
1717+
<refsect1>
1718+
<title>Description</title>
1719+
1720+
<para>
1721+
<function>SPI_execute_plan_with_receiver</function> executes a statement
1722+
prepared by <function>SPI_prepare</function>. This function is
1723+
equivalent to <function>SPI_execute_plan_with_paramlist</function>
1724+
except that, instead of always accumulating the result tuples into a
1725+
<varname>SPI_tuptable</varname> structure, tuples can be passed to a
1726+
caller-supplied <literal>DestReceiver</literal> object as they are
1727+
generated by the executor. This is particularly helpful for queries
1728+
that might generate many tuples, since the data can be processed
1729+
on-the-fly instead of being accumulated in memory.
1730+
</para>
1731+
</refsect1>
1732+
1733+
<refsect1>
1734+
<title>Arguments</title>
1735+
1736+
<variablelist>
1737+
<varlistentry>
1738+
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
1739+
<listitem>
1740+
<para>
1741+
prepared statement (returned by <function>SPI_prepare</function>)
1742+
</para>
1743+
</listitem>
1744+
</varlistentry>
1745+
1746+
<varlistentry>
1747+
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
1748+
<listitem>
1749+
<para>
1750+
data structure containing parameter types and values; NULL if none
1751+
</para>
1752+
</listitem>
1753+
</varlistentry>
1754+
1755+
<varlistentry>
1756+
<term><literal>bool <parameter>read_only</parameter></literal></term>
1757+
<listitem>
1758+
<para><literal>true</literal> for read-only execution</para>
1759+
</listitem>
1760+
</varlistentry>
1761+
1762+
<varlistentry>
1763+
<term><literal>long <parameter>count</parameter></literal></term>
1764+
<listitem>
1765+
<para>
1766+
maximum number of rows to return,
1767+
or <literal>0</literal> for no limit
1768+
</para>
1769+
</listitem>
1770+
</varlistentry>
1771+
1772+
<varlistentry>
1773+
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
1774+
<listitem>
1775+
<para>
1776+
<literal>DestReceiver</literal> object that will receive any tuples
1777+
emitted by the query; if NULL, this function is exactly equivalent to
1778+
<function>SPI_execute_plan_with_paramlist</function>
1779+
</para>
1780+
</listitem>
1781+
</varlistentry>
1782+
</variablelist>
1783+
</refsect1>
1784+
1785+
<refsect1>
1786+
<title>Return Value</title>
1787+
1788+
<para>
1789+
The return value is the same as for <function>SPI_execute_plan</function>.
1790+
</para>
1791+
1792+
<para>
1793+
When <parameter>dest</parameter> is NULL,
1794+
<varname>SPI_processed</varname> and
1795+
<varname>SPI_tuptable</varname> are set as in
1796+
<function>SPI_execute_plan</function>.
1797+
When <parameter>dest</parameter> is not NULL,
1798+
<varname>SPI_processed</varname> is set to zero and
1799+
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
1800+
is required, the caller's <literal>DestReceiver</literal> object must
1801+
calculate it.
1802+
</para>
1803+
</refsect1>
1804+
</refentry>
1805+
1806+
<!-- *********************************************** -->
1807+
15671808
<refentry id="spi-spi-execp">
15681809
<indexterm><primary>SPI_execp</primary></indexterm>
15691810

@@ -2041,6 +2282,114 @@ Portal SPI_cursor_open_with_paramlist(const char *<parameter>name</parameter>,
20412282

20422283
<!-- *********************************************** -->
20432284

2285+
<refentry id="spi-spi-cursor-parse-open-with-paramlist">
2286+
<indexterm><primary>SPI_cursor_parse_open_with_paramlist</primary></indexterm>
2287+
2288+
<refmeta>
2289+
<refentrytitle>SPI_cursor_parse_open_with_paramlist</refentrytitle>
2290+
<manvolnum>3</manvolnum>
2291+
</refmeta>
2292+
2293+
<refnamediv>
2294+
<refname>SPI_cursor_parse_open_with_paramlist</refname>
2295+
<refpurpose>set up a cursor using a query and parameters</refpurpose>
2296+
</refnamediv>
2297+
2298+
<refsynopsisdiv>
2299+
<synopsis>
2300+
Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</parameter>,
2301+
const char *<parameter>command</parameter>,
2302+
ParamListInfo <parameter>params</parameter>,
2303+
bool <parameter>read_only</parameter>,
2304+
int <parameter>cursorOptions</parameter>)
2305+
</synopsis>
2306+
</refsynopsisdiv>
2307+
2308+
<refsect1>
2309+
<title>Description</title>
2310+
2311+
<para>
2312+
<function>SPI_cursor_parse_open_with_paramlist</function> sets up a cursor
2313+
(internally, a portal) that will execute the specified query. This
2314+
function is equivalent to <function>SPI_cursor_open_with_args</function>
2315+
except that any parameters referenced by the query are provided by
2316+
a <literal>ParamListInfo</literal> object, rather than in ad-hoc arrays.
2317+
</para>
2318+
2319+
<para>
2320+
The <parameter>params</parameter> object should normally mark each
2321+
parameter with the <literal>PARAM_FLAG_CONST</literal> flag, since
2322+
a one-shot plan is always used for the query.
2323+
</para>
2324+
2325+
<para>
2326+
The passed-in parameter data will be copied into the cursor's portal, so it
2327+
can be freed while the cursor still exists.
2328+
</para>
2329+
</refsect1>
2330+
2331+
<refsect1>
2332+
<title>Arguments</title>
2333+
2334+
<variablelist>
2335+
<varlistentry>
2336+
<term><literal>const char * <parameter>name</parameter></literal></term>
2337+
<listitem>
2338+
<para>
2339+
name for portal, or <symbol>NULL</symbol> to let the system
2340+
select a name
2341+
</para>
2342+
</listitem>
2343+
</varlistentry>
2344+
2345+
<varlistentry>
2346+
<term><literal>const char * <parameter>command</parameter></literal></term>
2347+
<listitem>
2348+
<para>
2349+
command string
2350+
</para>
2351+
</listitem>
2352+
</varlistentry>
2353+
2354+
<varlistentry>
2355+
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
2356+
<listitem>
2357+
<para>
2358+
data structure containing parameter types and values; NULL if none
2359+
</para>
2360+
</listitem>
2361+
</varlistentry>
2362+
2363+
<varlistentry>
2364+
<term><literal>bool <parameter>read_only</parameter></literal></term>
2365+
<listitem>
2366+
<para><literal>true</literal> for read-only execution</para>
2367+
</listitem>
2368+
</varlistentry>
2369+
2370+
<varlistentry>
2371+
<term><literal>int <parameter>cursorOptions</parameter></literal></term>
2372+
<listitem>
2373+
<para>
2374+
integer bit mask of cursor options; zero produces default behavior
2375+
</para>
2376+
</listitem>
2377+
</varlistentry>
2378+
</variablelist>
2379+
</refsect1>
2380+
2381+
<refsect1>
2382+
<title>Return Value</title>
2383+
2384+
<para>
2385+
Pointer to portal containing the cursor. Note there is no error
2386+
return convention; any error will be reported via <function>elog</function>.
2387+
</para>
2388+
</refsect1>
2389+
</refentry>
2390+
2391+
<!-- *********************************************** -->
2392+
20442393
<refentry id="spi-spi-cursor-find">
20452394
<indexterm><primary>SPI_cursor_find</primary></indexterm>
20462395

src/backend/commands/portalcmds.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ PersistHoldablePortal(Portal portal)
383383
SetTuplestoreDestReceiverParams(queryDesc->dest,
384384
portal->holdStore,
385385
portal->holdContext,
386-
true);
386+
true,
387+
NULL,
388+
NULL);
387389

388390
/* Fetch the result set into the tuplestore */
389391
ExecutorRun(queryDesc, ForwardScanDirection, 0L, false);

0 commit comments

Comments
 (0)