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 ee8a80a3921..b98c18199b6 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -17,6 +17,8 @@ EXTENSION = postgres_fdw DATA = postgres_fdw--1.0.sql REGRESS = postgres_fdw +ISOLATION = eval_plan_qual +ISOLATION_OPTS = --load-extension=postgres_fdw ifdef USE_PGXS PG_CONFIG = pg_config 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..7f55c97ab4a --- /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; + +step s0_commit: COMMIT; +step s1_tuplock: <... completed> +QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- +LockRows + Output: a.i, ((SubPlan 1)), a.ctid + -> Seq Scan on public.a + Output: a.i, (SubPlan 1), a.ctid + SubPlan 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/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/backend/executor/execScan.c b/src/backend/executor/execScan.c index 642805d90cd..5fa44a0c5e1 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -55,16 +55,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]) {