Skip to content

Commit ee895a6

Browse files
committed
Improve performance of repeated CALLs within plpgsql procedures.
This patch essentially is cleaning up technical debt left behind by the original implementation of plpgsql procedures, particularly commit d92bc83. That patch (or more precisely, follow-on patches fixing its worst bugs) forced us to re-plan CALL and DO statements each time through, if we're in a non-atomic context. That wasn't for any fundamental reason, but just because use of a saved plan requires having a ResourceOwner to hold a reference count for the plan, and we had no suitable resowner at hand, nor would the available APIs support using one if we did. While it's not that expensive to create a "plan" for CALL/DO, the cycles do add up in repeated executions. This patch therefore makes the following API changes: * GetCachedPlan/ReleaseCachedPlan are modified to let the caller specify which resowner to use to pin the plan, rather than forcing use of CurrentResourceOwner. * spi.c gains a "SPI_execute_plan_extended" entry point that lets callers say which resowner to use to pin the plan. This borrows the idea of an options struct from the recently added SPI_prepare_extended, hopefully allowing future options to be added without more API breaks. This supersedes SPI_execute_plan_with_paramlist (which I've marked deprecated) as well as SPI_execute_plan_with_receiver (which is new in v14, so I just took it out altogether). * I also took the opportunity to remove the crude hack of letting plpgsql reach into SPI private data structures to mark SPI plans as "no_snapshot". It's better to treat that as an option of SPI_prepare_extended. Now, when running a non-atomic procedure or DO block that contains any CALL or DO commands, plpgsql creates a ResourceOwner that will be used to pin the plans of the CALL/DO commands. (In an atomic context, we just use CurrentResourceOwner, as before.) Having done this, we can just save CALL/DO plans normally, whether or not they are used across transaction boundaries. This seems to be good for something like 2X speedup of a CALL of a trivial procedure with a few simple argument expressions. By restricting the creation of an extra ResourceOwner like this, there's essentially zero penalty in cases that can't benefit. Pavel Stehule, with some further hacking by me Discussion: https://postgr.es/m/CAFj8pRCLPdDAETvR7Po7gC5y_ibkn_-bOzbeJb39WHms01194Q@mail.gmail.com
1 parent 55ef855 commit ee895a6

File tree

15 files changed

+461
-367
lines changed

15 files changed

+461
-367
lines changed

doc/src/sgml/spi.sgml

+111-54
Original file line numberDiff line numberDiff line change
@@ -1722,40 +1722,53 @@ int SPI_execute_plan(SPIPlanPtr <parameter>plan</parameter>, Datum * <parameter>
17221722

17231723
<!-- *********************************************** -->
17241724

1725-
<refentry id="spi-spi-execute-plan-with-paramlist">
1726-
<indexterm><primary>SPI_execute_plan_with_paramlist</primary></indexterm>
1725+
<refentry id="spi-spi-execute-plan-extended">
1726+
<indexterm><primary>SPI_execute_plan_extended</primary></indexterm>
17271727

17281728
<refmeta>
1729-
<refentrytitle>SPI_execute_plan_with_paramlist</refentrytitle>
1729+
<refentrytitle>SPI_execute_plan_extended</refentrytitle>
17301730
<manvolnum>3</manvolnum>
17311731
</refmeta>
17321732

17331733
<refnamediv>
1734-
<refname>SPI_execute_plan_with_paramlist</refname>
1734+
<refname>SPI_execute_plan_extended</refname>
17351735
<refpurpose>execute a statement prepared by <function>SPI_prepare</function></refpurpose>
17361736
</refnamediv>
17371737

17381738
<refsynopsisdiv>
17391739
<synopsis>
1740-
int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
1741-
ParamListInfo <parameter>params</parameter>,
1742-
bool <parameter>read_only</parameter>,
1743-
long <parameter>count</parameter>)
1740+
int SPI_execute_plan_extended(SPIPlanPtr <parameter>plan</parameter>,
1741+
const SPIExecuteOptions * <parameter>options</parameter>)
17441742
</synopsis>
17451743
</refsynopsisdiv>
17461744

17471745
<refsect1>
17481746
<title>Description</title>
17491747

17501748
<para>
1751-
<function>SPI_execute_plan_with_paramlist</function> executes a statement
1752-
prepared by <function>SPI_prepare</function>.
1753-
This function is equivalent to <function>SPI_execute_plan</function>
1749+
<function>SPI_execute_plan_extended</function> executes a statement
1750+
prepared by <function>SPI_prepare</function> or one of its siblings.
1751+
This function is equivalent to <function>SPI_execute_plan</function>,
17541752
except that information about the parameter values to be passed to the
1755-
query is presented differently. The <literal>ParamListInfo</literal>
1756-
representation can be convenient for passing down values that are
1757-
already available in that format. It also supports use of dynamic
1758-
parameter sets via hook functions specified in <literal>ParamListInfo</literal>.
1753+
query is presented differently, and additional execution-controlling
1754+
options can be passed.
1755+
</para>
1756+
1757+
<para>
1758+
Query parameter values are represented by
1759+
a <literal>ParamListInfo</literal> struct, which is convenient for passing
1760+
down values that are already available in that format. Dynamic parameter
1761+
sets can also be used, via hook functions specified
1762+
in <literal>ParamListInfo</literal>.
1763+
</para>
1764+
1765+
<para>
1766+
Also, instead of always accumulating the result tuples into a
1767+
<varname>SPI_tuptable</varname> structure, tuples can be passed to a
1768+
caller-supplied <literal>DestReceiver</literal> object as they are
1769+
generated by the executor. This is particularly helpful for queries
1770+
that might generate many tuples, since the data can be processed
1771+
on-the-fly instead of being accumulated in memory.
17591772
</para>
17601773
</refsect1>
17611774

@@ -1772,11 +1785,30 @@ int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
17721785
</listitem>
17731786
</varlistentry>
17741787

1788+
<varlistentry>
1789+
<term><literal>const SPIExecuteOptions * <parameter>options</parameter></literal></term>
1790+
<listitem>
1791+
<para>
1792+
struct containing optional arguments
1793+
</para>
1794+
</listitem>
1795+
</varlistentry>
1796+
</variablelist>
1797+
1798+
<para>
1799+
Callers should always zero out the entire <parameter>options</parameter>
1800+
struct, then fill whichever fields they want to set. This ensures forward
1801+
compatibility of code, since any fields that are added to the struct in
1802+
future will be defined to behave backwards-compatibly if they are zero.
1803+
The currently available <parameter>options</parameter> fields are:
1804+
</para>
1805+
1806+
<variablelist>
17751807
<varlistentry>
17761808
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
17771809
<listitem>
17781810
<para>
1779-
data structure containing parameter types and values; NULL if none
1811+
data structure containing query parameter types and values; NULL if none
17801812
</para>
17811813
</listitem>
17821814
</varlistentry>
@@ -1789,14 +1821,47 @@ int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
17891821
</varlistentry>
17901822

17911823
<varlistentry>
1792-
<term><literal>long <parameter>count</parameter></literal></term>
1824+
<term><literal>bool <parameter>no_snapshots</parameter></literal></term>
1825+
<listitem>
1826+
<para>
1827+
<literal>true</literal> prevents SPI from managing snapshots for
1828+
execution of the query; use with extreme caution
1829+
</para>
1830+
</listitem>
1831+
</varlistentry>
1832+
1833+
<varlistentry>
1834+
<term><literal>uint64 <parameter>tcount</parameter></literal></term>
17931835
<listitem>
17941836
<para>
17951837
maximum number of rows to return,
17961838
or <literal>0</literal> for no limit
17971839
</para>
17981840
</listitem>
17991841
</varlistentry>
1842+
1843+
<varlistentry>
1844+
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
1845+
<listitem>
1846+
<para>
1847+
<literal>DestReceiver</literal> object that will receive any tuples
1848+
emitted by the query; if NULL, result tuples are accumulated into
1849+
a <varname>SPI_tuptable</varname> structure, as
1850+
in <function>SPI_execute_plan</function>
1851+
</para>
1852+
</listitem>
1853+
</varlistentry>
1854+
1855+
<varlistentry>
1856+
<term><literal>ResourceOwner <parameter>owner</parameter></literal></term>
1857+
<listitem>
1858+
<para>
1859+
The resource owner that will hold a reference count on the plan while
1860+
it is executed. If NULL, CurrentResourceOwner is used. Ignored for
1861+
non-saved plans, as SPI does not acquire reference counts on those.
1862+
</para>
1863+
</listitem>
1864+
</varlistentry>
18001865
</variablelist>
18011866
</refsect1>
18021867

@@ -1808,51 +1873,60 @@ int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
18081873
</para>
18091874

18101875
<para>
1876+
When <parameter>dest</parameter> is NULL,
18111877
<varname>SPI_processed</varname> and
18121878
<varname>SPI_tuptable</varname> are set as in
1813-
<function>SPI_execute_plan</function> if successful.
1879+
<function>SPI_execute_plan</function>.
1880+
When <parameter>dest</parameter> is not NULL,
1881+
<varname>SPI_processed</varname> is set to zero and
1882+
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
1883+
is required, the caller's <literal>DestReceiver</literal> object must
1884+
calculate it.
18141885
</para>
18151886
</refsect1>
18161887
</refentry>
18171888

18181889
<!-- *********************************************** -->
18191890

1820-
<refentry id="spi-spi-execute-plan-with-receiver">
1821-
<indexterm><primary>SPI_execute_plan_with_receiver</primary></indexterm>
1891+
<refentry id="spi-spi-execute-plan-with-paramlist">
1892+
<indexterm><primary>SPI_execute_plan_with_paramlist</primary></indexterm>
18221893

18231894
<refmeta>
1824-
<refentrytitle>SPI_execute_plan_with_receiver</refentrytitle>
1895+
<refentrytitle>SPI_execute_plan_with_paramlist</refentrytitle>
18251896
<manvolnum>3</manvolnum>
18261897
</refmeta>
18271898

18281899
<refnamediv>
1829-
<refname>SPI_execute_plan_with_receiver</refname>
1900+
<refname>SPI_execute_plan_with_paramlist</refname>
18301901
<refpurpose>execute a statement prepared by <function>SPI_prepare</function></refpurpose>
18311902
</refnamediv>
18321903

18331904
<refsynopsisdiv>
18341905
<synopsis>
1835-
int SPI_execute_plan_with_receiver(SPIPlanPtr <parameter>plan</parameter>,
1836-
ParamListInfo <parameter>params</parameter>,
1837-
bool <parameter>read_only</parameter>,
1838-
long <parameter>count</parameter>,
1839-
DestReceiver *<parameter>dest</parameter>)
1906+
int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
1907+
ParamListInfo <parameter>params</parameter>,
1908+
bool <parameter>read_only</parameter>,
1909+
long <parameter>count</parameter>)
18401910
</synopsis>
18411911
</refsynopsisdiv>
18421912

18431913
<refsect1>
18441914
<title>Description</title>
18451915

18461916
<para>
1847-
<function>SPI_execute_plan_with_receiver</function> executes a statement
1848-
prepared by <function>SPI_prepare</function>. This function is
1849-
equivalent to <function>SPI_execute_plan_with_paramlist</function>
1850-
except that, instead of always accumulating the result tuples into a
1851-
<varname>SPI_tuptable</varname> structure, tuples can be passed to a
1852-
caller-supplied <literal>DestReceiver</literal> object as they are
1853-
generated by the executor. This is particularly helpful for queries
1854-
that might generate many tuples, since the data can be processed
1855-
on-the-fly instead of being accumulated in memory.
1917+
<function>SPI_execute_plan_with_paramlist</function> executes a statement
1918+
prepared by <function>SPI_prepare</function>.
1919+
This function is equivalent to <function>SPI_execute_plan</function>
1920+
except that information about the parameter values to be passed to the
1921+
query is presented differently. The <literal>ParamListInfo</literal>
1922+
representation can be convenient for passing down values that are
1923+
already available in that format. It also supports use of dynamic
1924+
parameter sets via hook functions specified in <literal>ParamListInfo</literal>.
1925+
</para>
1926+
1927+
<para>
1928+
This function is now deprecated in favor
1929+
of <function>SPI_execute_plan_extended</function>.
18561930
</para>
18571931
</refsect1>
18581932

@@ -1894,17 +1968,6 @@ int SPI_execute_plan_with_receiver(SPIPlanPtr <parameter>plan</parameter>,
18941968
</para>
18951969
</listitem>
18961970
</varlistentry>
1897-
1898-
<varlistentry>
1899-
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
1900-
<listitem>
1901-
<para>
1902-
<literal>DestReceiver</literal> object that will receive any tuples
1903-
emitted by the query; if NULL, this function is exactly equivalent to
1904-
<function>SPI_execute_plan_with_paramlist</function>
1905-
</para>
1906-
</listitem>
1907-
</varlistentry>
19081971
</variablelist>
19091972
</refsect1>
19101973

@@ -1916,15 +1979,9 @@ int SPI_execute_plan_with_receiver(SPIPlanPtr <parameter>plan</parameter>,
19161979
</para>
19171980

19181981
<para>
1919-
When <parameter>dest</parameter> is NULL,
19201982
<varname>SPI_processed</varname> and
19211983
<varname>SPI_tuptable</varname> are set as in
1922-
<function>SPI_execute_plan</function>.
1923-
When <parameter>dest</parameter> is not NULL,
1924-
<varname>SPI_processed</varname> is set to zero and
1925-
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
1926-
is required, the caller's <literal>DestReceiver</literal> object must
1927-
calculate it.
1984+
<function>SPI_execute_plan</function> if successful.
19281985
</para>
19291986
</refsect1>
19301987
</refentry>

src/backend/commands/prepare.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ ExecuteQuery(ParseState *pstate,
230230
entry->plansource->query_string);
231231

232232
/* Replan if needed, and increment plan refcount for portal */
233-
cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
233+
cplan = GetCachedPlan(entry->plansource, paramLI, NULL, NULL);
234234
plan_list = cplan->stmt_list;
235235

236236
/*
@@ -651,7 +651,8 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
651651
}
652652

653653
/* Replan if needed, and acquire a transient refcount */
654-
cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
654+
cplan = GetCachedPlan(entry->plansource, paramLI,
655+
CurrentResourceOwner, queryEnv);
655656

656657
INSTR_TIME_SET_CURRENT(planduration);
657658
INSTR_TIME_SUBTRACT(planduration, planstart);
@@ -687,7 +688,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
687688
if (estate)
688689
FreeExecutorState(estate);
689690

690-
ReleaseCachedPlan(cplan, true);
691+
ReleaseCachedPlan(cplan, CurrentResourceOwner);
691692
}
692693

693694
/*

0 commit comments

Comments
 (0)