summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/postgres_fdw/.gitignore2
-rw-r--r--contrib/postgres_fdw/Makefile2
-rw-r--r--contrib/postgres_fdw/expected/eval_plan_qual.out37
-rw-r--r--contrib/postgres_fdw/meson.build6
-rw-r--r--contrib/postgres_fdw/specs/eval_plan_qual.spec55
-rw-r--r--src/include/executor/execScan.h22
6 files changed, 117 insertions, 7 deletions
diff --git a/contrib/postgres_fdw/.gitignore b/contrib/postgres_fdw/.gitignore
index 5dcb3ff9723..b4903eba657 100644
--- a/contrib/postgres_fdw/.gitignore
+++ b/contrib/postgres_fdw/.gitignore
@@ -1,4 +1,6 @@
# Generated subdirectories
/log/
/results/
+/output_iso/
/tmp_check/
+/tmp_check_iso/
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index adfbd2ef758..8eaf4d263b6 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -17,6 +17,8 @@ EXTENSION = postgres_fdw
DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw query_cancel
+ISOLATION = eval_plan_qual
+ISOLATION_OPTS = --load-extension=postgres_fdw
TAP_TESTS = 1
ifdef USE_PGXS
diff --git a/contrib/postgres_fdw/expected/eval_plan_qual.out b/contrib/postgres_fdw/expected/eval_plan_qual.out
new file mode 100644
index 00000000000..f3e3a22b336
--- /dev/null
+++ b/contrib/postgres_fdw/expected/eval_plan_qual.out
@@ -0,0 +1,37 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s0_begin s0_update s1_begin s1_tuplock s0_commit s1_commit
+step s0_begin: BEGIN ISOLATION LEVEL READ COMMITTED;
+step s0_update: UPDATE a SET i = i + 1;
+step s1_begin: BEGIN ISOLATION LEVEL READ COMMITTED;
+step s1_tuplock:
+ -- Verify if the sub-select has a foreign-join plan
+ EXPLAIN (VERBOSE, COSTS OFF)
+ SELECT a.i,
+ (SELECT 1 FROM fb, fc WHERE a.i = fb.i AND fb.i = fc.i)
+ FROM a FOR UPDATE;
+ SELECT a.i,
+ (SELECT 1 FROM fb, fc WHERE a.i = fb.i AND fb.i = fc.i)
+ FROM a FOR UPDATE;
+ <waiting ...>
+step s0_commit: COMMIT;
+step s1_tuplock: <... completed>
+QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------------------
+LockRows
+ Output: a.i, ((SubPlan expr_1)), a.ctid
+ -> Seq Scan on public.a
+ Output: a.i, (SubPlan expr_1), a.ctid
+ SubPlan expr_1
+ -> Foreign Scan
+ Output: 1
+ Relations: (public.fb) INNER JOIN (public.fc)
+ Remote SQL: SELECT NULL FROM (public.b r1 INNER JOIN public.c r2 ON (((r2.i = $1::integer)) AND ((r1.i = $1::integer))))
+(9 rows)
+
+i|?column?
+-+--------
+2|
+(1 row)
+
+step s1_commit: COMMIT;
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index 5c11bc6496f..aac89ffdde8 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -41,6 +41,12 @@ tests += {
],
'regress_args': ['--dlpath', meson.project_build_root() / 'src/test/regress'],
},
+ 'isolation': {
+ 'specs': [
+ 'eval_plan_qual',
+ ],
+ 'regress_args': ['--load-extension=postgres_fdw'],
+ },
'tap': {
'tests': [
't/001_auth_scram.pl',
diff --git a/contrib/postgres_fdw/specs/eval_plan_qual.spec b/contrib/postgres_fdw/specs/eval_plan_qual.spec
new file mode 100644
index 00000000000..30a83e04058
--- /dev/null
+++ b/contrib/postgres_fdw/specs/eval_plan_qual.spec
@@ -0,0 +1,55 @@
+# Tests for the EvalPlanQual mechanism involving foreign tables
+
+setup
+{
+ DO $d$
+ BEGIN
+ EXECUTE $$CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw
+ OPTIONS (dbname '$$||current_database()||$$',
+ port '$$||current_setting('port')||$$'
+ )$$;
+ END;
+ $d$;
+ CREATE USER MAPPING FOR PUBLIC SERVER loopback;
+
+ CREATE TABLE a (i int);
+ CREATE TABLE b (i int);
+ CREATE TABLE c (i int);
+ CREATE FOREIGN TABLE fb (i int) SERVER loopback OPTIONS (table_name 'b');
+ CREATE FOREIGN TABLE fc (i int) SERVER loopback OPTIONS (table_name 'c');
+
+ INSERT INTO a VALUES (1);
+ INSERT INTO b VALUES (1);
+ INSERT INTO c VALUES (1);
+}
+
+teardown
+{
+ DROP TABLE a;
+ DROP TABLE b;
+ DROP TABLE c;
+ DROP SERVER loopback CASCADE;
+}
+
+session s0
+step s0_begin { BEGIN ISOLATION LEVEL READ COMMITTED; }
+step s0_update { UPDATE a SET i = i + 1; }
+step s0_commit { COMMIT; }
+
+session s1
+step s1_begin { BEGIN ISOLATION LEVEL READ COMMITTED; }
+step s1_tuplock {
+ -- Verify if the sub-select has a foreign-join plan
+ EXPLAIN (VERBOSE, COSTS OFF)
+ SELECT a.i,
+ (SELECT 1 FROM fb, fc WHERE a.i = fb.i AND fb.i = fc.i)
+ FROM a FOR UPDATE;
+ SELECT a.i,
+ (SELECT 1 FROM fb, fc WHERE a.i = fb.i AND fb.i = fc.i)
+ FROM a FOR UPDATE;
+}
+step s1_commit { COMMIT; }
+
+# This test exercises EvalPlanQual with a SubLink sub-select (which should
+# be unaffected by any EPQ recheck behavior in the outer query).
+permutation s0_begin s0_update s1_begin s1_tuplock s0_commit s1_commit
diff --git a/src/include/executor/execScan.h b/src/include/executor/execScan.h
index 837ea7785bb..2003cbc7ed5 100644
--- a/src/include/executor/execScan.h
+++ b/src/include/executor/execScan.h
@@ -49,16 +49,24 @@ ExecScanFetch(ScanState *node,
{
/*
* This is a ForeignScan or CustomScan which has pushed down a
- * join to the remote side. The recheck method is responsible not
- * only for rechecking the scan/join quals but also for storing
- * the correct tuple in the slot.
+ * join to the remote side. If it is a descendant node in the EPQ
+ * recheck plan tree, run the recheck method function. Otherwise,
+ * run the access method function below.
*/
+ if (bms_is_member(epqstate->epqParam, node->ps.plan->extParam))
+ {
+ /*
+ * The recheck method is responsible not only for rechecking
+ * the scan/join quals but also for storing the correct tuple
+ * in the slot.
+ */
- TupleTableSlot *slot = node->ss_ScanTupleSlot;
+ TupleTableSlot *slot = node->ss_ScanTupleSlot;
- if (!(*recheckMtd) (node, slot))
- ExecClearTuple(slot); /* would not be returned by scan */
- return slot;
+ if (!(*recheckMtd) (node, slot))
+ ExecClearTuple(slot); /* would not be returned by scan */
+ return slot;
+ }
}
else if (epqstate->relsubs_done[scanrelid - 1])
{