summaryrefslogtreecommitdiff
path: root/doc/src/sgml
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-06-08 17:50:15 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-06-08 17:50:15 -0400
commitc3b5082685dd6f9f6b76c227e04ae8dca720acc5 (patch)
tree3c3874fcaaeb56394d4ca9c7d9da5ba91c469112 /doc/src/sgml
parentb7684bd105657813e9366f8d664c22bcfb0e9f42 (diff)
downloadpostgresql-c3b5082685dd6f9f6b76c227e04ae8dca720acc5.tar.gz
Avoid misbehavior when persisting a non-stable cursor.
PersistHoldablePortal has long assumed that it should store the entire output of the query-to-be-persisted, which requires rewinding and re-reading the output. This is problematic if the query is not stable: we might get different row contents, or even a different number of rows, which'd confuse the cursor state mightily. In the case where the cursor is NO SCROLL, this is very easy to solve: just store the remaining query output, without any rewinding, and tweak the portal's cursor state to match. Aside from removing the semantic problem, this could be significantly more efficient than storing the whole output. If the cursor is scrollable, there's not much we can do, but it was already the case that scrolling a volatile query's result was pretty unsafe. We can just document more clearly that getting correct results from that is not guaranteed. There are already prohibitions in place on using SCROLL with FOR UPDATE/SHARE, which is one way for a SELECT query to have non-stable results. We could imagine prohibiting SCROLL when the query contains volatile functions, but that would be expensive to enforce. Moreover, it could break applications that work just fine, if they have functions that are in fact stable but the user neglected to mark them so. So settle for documenting the hazard. While this problem has existed in some guise for a long time, it got a lot worse in v11, which introduced the possibility of persisting plpgsql cursors (perhaps implicit ones) even when they violate the rules for what can be marked WITH HOLD. Hence, I've chosen to back-patch to v11 but not further. Per bug #17050 from Алексей Булгаков. Discussion: https://postgr.es/m/17050-f77aa827dc85247c@postgresql.org
Diffstat (limited to 'doc/src/sgml')
-rw-r--r--doc/src/sgml/plpgsql.sgml9
-rw-r--r--doc/src/sgml/ref/declare.sgml8
2 files changed, 14 insertions, 3 deletions
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index afa96ebb29..887a48f10c 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -3005,6 +3005,15 @@ DECLARE
is said to be <firstterm>unbound</firstterm> since it is not bound to
any particular query.
</para>
+
+ <para>
+ The <literal>SCROLL</literal> option cannot be used when the cursor's
+ query uses <literal>FOR UPDATE/SHARE</literal>. Also, it is
+ best to use <literal>NO SCROLL</literal> with a query that involves
+ volatile functions. The implementation of <literal>SCROLL</literal>
+ assumes that re-reading the query's output will give consistent
+ results, which a volatile function might not do.
+ </para>
</sect2>
<sect2 id="plpgsql-cursor-opening">
diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml
index 34ca9df243..2bcd101929 100644
--- a/doc/src/sgml/ref/declare.sgml
+++ b/doc/src/sgml/ref/declare.sgml
@@ -228,12 +228,14 @@ DECLARE <replaceable class="parameter">name</replaceable> [ BINARY ] [ INSENSITI
<caution>
<para>
- Scrollable and <literal>WITH HOLD</literal> cursors may give unexpected
+ Scrollable cursors may give unexpected
results if they invoke any volatile functions (see <xref
linkend="xfunc-volatility"/>). When a previously fetched row is
re-fetched, the functions might be re-executed, perhaps leading to
- results different from the first time. One workaround for such cases
- is to declare the cursor <literal>WITH HOLD</literal> and commit the
+ results different from the first time. It's best to
+ specify <literal>NO SCROLL</literal> for a query involving volatile
+ functions. If that is not practical, one workaround
+ is to declare the cursor <literal>SCROLL WITH HOLD</literal> and commit the
transaction before reading any rows from it. This will force the
entire output of the cursor to be materialized in temporary storage,
so that volatile functions are executed exactly once for each row.