aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-06-22 17:48:39 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-06-22 17:48:39 -0400
commitd102aafb6259a6a412803d4b1d8c4f00aa17f67e (patch)
tree4a882a60ad10b72d6b9bf1e817de9f45b1d123e2 /src
parent8e638845ff6bb255ad1dea15831089a234535391 (diff)
downloadpostgresql-d102aafb6259a6a412803d4b1d8c4f00aa17f67e.tar.gz
postgresql-d102aafb6259a6a412803d4b1d8c4f00aa17f67e.zip
Restore the portal-level snapshot for simple expressions, too.
Commits 84f5c2908 et al missed the need to cover plpgsql's "simple expression" code path. If the first thing we execute after a COMMIT/ROLLBACK is one of those, rather than a full-fledged SPI command, we must explicitly do EnsurePortalSnapshotExists() to make sure we have an outer snapshot. Note that it wouldn't be good enough to just push a snapshot for the duration of the expression execution: what comes back might be toasted, so we'd better have a snapshot protecting it. The test case demonstrating this fact cheats a bit by marking a SQL function immutable even though it fetches from a table. That's nothing that users haven't been seen to do, though. Per report from Jim Nasby. Back-patch to v11, like the previous fix. Discussion: https://postgr.es/m/378885e4-f85f-fc28-6c91-c4d1c080bf26@amazon.com
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/expected/plpgsql_transaction.out18
-rw-r--r--src/pl/plpgsql/src/pl_exec.c10
-rw-r--r--src/pl/plpgsql/src/sql/plpgsql_transaction.sql21
3 files changed, 49 insertions, 0 deletions
diff --git a/src/pl/plpgsql/src/expected/plpgsql_transaction.out b/src/pl/plpgsql/src/expected/plpgsql_transaction.out
index 76cbdca0c56..57ab0bc0e7d 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_transaction.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_transaction.out
@@ -430,6 +430,24 @@ SELECT * FROM test1;
---+---
(0 rows)
+-- detoast result of simple expression after commit
+CREATE TEMP TABLE test4(f1 text);
+ALTER TABLE test4 ALTER COLUMN f1 SET STORAGE EXTERNAL; -- disable compression
+INSERT INTO test4 SELECT repeat('xyzzy', 2000);
+-- immutable mark is a bit of a lie, but it serves to make call a simple expr
+-- that will return a still-toasted value
+CREATE FUNCTION data_source(i int) RETURNS TEXT LANGUAGE sql
+AS 'select f1 from test4' IMMUTABLE;
+DO $$
+declare x text;
+begin
+ for i in 1..3 loop
+ x := data_source(i);
+ commit;
+ end loop;
+ raise notice 'length(x) = %', length(x);
+end $$;
+NOTICE: length(x) = 10000
-- operations on composite types vs. internal transactions
DO LANGUAGE plpgsql $$
declare
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0ce382e1232..96bb77e0b1e 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -38,6 +38,7 @@
#include "plpgsql.h"
#include "storage/proc.h"
#include "tcop/cmdtag.h"
+#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/array.h"
@@ -5959,6 +5960,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
return false;
/*
+ * Ensure that there's a portal-level snapshot, in case this simple
+ * expression is the first thing evaluated after a COMMIT or ROLLBACK.
+ * We'd have to do this anyway before executing the expression, so we
+ * might as well do it now to ensure that any possible replanning doesn't
+ * need to take a new snapshot.
+ */
+ EnsurePortalSnapshotExists();
+
+ /*
* Check to see if the cached plan has been invalidated. If not, and this
* is the first use in the current transaction, save a plan refcount in
* the simple-expression resowner.
diff --git a/src/pl/plpgsql/src/sql/plpgsql_transaction.sql b/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
index cc26788b9ae..8e4783c9a51 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
@@ -354,6 +354,27 @@ $$;
SELECT * FROM test1;
+-- detoast result of simple expression after commit
+CREATE TEMP TABLE test4(f1 text);
+ALTER TABLE test4 ALTER COLUMN f1 SET STORAGE EXTERNAL; -- disable compression
+INSERT INTO test4 SELECT repeat('xyzzy', 2000);
+
+-- immutable mark is a bit of a lie, but it serves to make call a simple expr
+-- that will return a still-toasted value
+CREATE FUNCTION data_source(i int) RETURNS TEXT LANGUAGE sql
+AS 'select f1 from test4' IMMUTABLE;
+
+DO $$
+declare x text;
+begin
+ for i in 1..3 loop
+ x := data_source(i);
+ commit;
+ end loop;
+ raise notice 'length(x) = %', length(x);
+end $$;
+
+
-- operations on composite types vs. internal transactions
DO LANGUAGE plpgsql $$
declare