aboutsummaryrefslogtreecommitdiff
path: root/src/include/utils/portal.h
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-08-07 17:46:08 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-08-07 17:46:08 -0400
commit9ee1cf04ab6bcefe03a11837b53f29ca9dc24c7a (patch)
tree5a8246dfc2d7402b01820f9b6794d995a007fcf1 /src/include/utils/portal.h
parent07a601eedab7c5fa4d62055fa3efacef2f38e446 (diff)
downloadpostgresql-9ee1cf04ab6bcefe03a11837b53f29ca9dc24c7a.tar.gz
postgresql-9ee1cf04ab6bcefe03a11837b53f29ca9dc24c7a.zip
Fix TOAST access failure in RETURNING queries.
Discussion of commit 3e2f3c2e4 exposed a problem that is of longer standing: since we don't detoast data while sticking it into a portal's holdStore for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT queries, and we release the query's snapshot as soon as we're done loading the holdStore, later readout of the holdStore can do TOAST fetches against data that can no longer be seen by any of the session's live snapshots. This means that a concurrent VACUUM could remove the TOAST data before we can fetch it. Commit 3e2f3c2e4 exposed the problem by showing that sometimes we had *no* live snapshots while fetching TOAST data, but we'd be at risk anyway. I believe this code was all right when written, because our management of a session's exposed xmin was such that the TOAST references were safe until end of transaction. But that's no longer true now that we can advance or clear our PGXACT.xmin intra-transaction. To fix, copy the query's snapshot during FillPortalStore() and save it in the Portal; release it only when the portal is dropped. This essentially implements a policy that we must hold a relevant snapshot whenever we access potentially-toasted data. We had already come to that conclusion in other places, cf commits 08e261cbc94ce9a7 and ec543db77b6b72f2. I'd have liked to add a regression test case for this, but I didn't see a way to make one that's not unreasonably bloated; it seems to require returning a toasted value to the client, and those will be big. In passing, improve PortalRunUtility() so that it positively verifies that its ending PopActiveSnapshot() call will pop the expected snapshot, removing a rather shaky assumption about which utility commands might do their own PopActiveSnapshot(). There's no known bug here, but now that we're actively referencing the snapshot it's almost free to make this code a bit more bulletproof. We might want to consider back-patching something like this into older branches, but it would be prudent to let it prove itself more in HEAD beforehand. Discussion: <87vazemeda.fsf@credativ.de>
Diffstat (limited to 'src/include/utils/portal.h')
-rw-r--r--src/include/utils/portal.h10
1 files changed, 10 insertions, 0 deletions
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 7250c9c1bb3..c1d93a96bb4 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -163,6 +163,16 @@ typedef struct PortalData
MemoryContext holdContext; /* memory containing holdStore */
/*
+ * Snapshot under which tuples in the holdStore were read. We must keep a
+ * reference to this snapshot if there is any possibility that the tuples
+ * contain TOAST references, because releasing the snapshot could allow
+ * recently-dead rows to be vacuumed away, along with any toast data
+ * belonging to them. In the case of a held cursor, we avoid needing to
+ * keep such a snapshot by forcibly detoasting the data.
+ */
+ Snapshot holdSnapshot; /* registered snapshot, or NULL if none */
+
+ /*
* atStart, atEnd and portalPos indicate the current cursor position.
* portalPos is zero before the first row, N after fetching N'th row of
* query. After we run off the end, portalPos = # of rows in query, and