1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

postgres_fdw: Fix flaky push down FUNCTION RTE test

The unnest() test introduced in 0ee83dd4a9 did not produce a
deterministic EXPLAIN plan output.

The two sides of this test's nested loop join cost about the same, so
which one ended up as the outer wasn't stable and could flip between
platforms.

Use an equality predicate on ft1.c3 instead of a range one.  That
restricts ft1 to a single row, so it's clearly the outer of the nested
loop and the Materialize on the inner side goes away, giving a stable
plan.

Author: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Discussion: https://postgr.es/m/CAA5RZ0uC%3DGOihOKcF7o3JVETF3e-Fkc4OUO5NbqMEKCTeSN-dQ%40mail.gmail.com
This commit is contained in:
Alexander Korotkov
2026-08-24 18:50:02 +03:00
parent 29636b49a7
commit 0fd1609afe
2 changed files with 15 additions and 17 deletions

View File

@@ -3128,28 +3128,26 @@ WHERE t1.c1 = u.id AND t2.c1 = u.id;
Remote SQL: SELECT c1 FROM "S 1"."T 4"
(12 rows)
-- Selective predicate on ft1.c3 (not in the eqclass) shrinks ft1 to a
-- handful of remote rows; now ft6 is effectively the bigger side and the
-- function is absorbed into ft6 instead.
-- Equality on ft1.c3 (not in the eqclass) restricts ft1 to a single remote row;
-- now ft6 is effectively the bigger side and the function is absorbed into
-- ft6 instead.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1
FROM ft1 t1, ft6 t2, unnest(ARRAY[3, 6, 9, 12, 15, 18]::int[]) AS u(id)
WHERE t1.c1 = u.id AND t2.c1 = u.id AND t1.c3 < '00010';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
WHERE t1.c1 = u.id AND t2.c1 = u.id AND t1.c3 = '00009';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop
Output: t1.c1, t2.c1
Join Filter: (t1.c1 = u.id)
-> Foreign Scan on public.ft1 t1
Output: t1.c1
Remote SQL: SELECT "C 1" FROM "S 1"."T 1" WHERE ((c3 < '00010'))
-> Materialize
Remote SQL: SELECT "C 1" FROM "S 1"."T 1" WHERE ((c3 = '00009'))
-> Foreign Scan
Output: t2.c1, u.id
-> Foreign Scan
Output: t2.c1, u.id
Relations: (public.ft6 t2) INNER JOIN (pg_catalog.unnest() u)
Remote SQL: SELECT r2.c1, f3.c1 FROM ("S 1"."T 4" r2 INNER JOIN unnest('{3,6,9,12,15,18}'::integer[]) f3(c1) ON (((r2.c1 = f3.c1))))
(12 rows)
Relations: (public.ft6 t2) INNER JOIN (pg_catalog.unnest() u)
Remote SQL: SELECT r2.c1, f3.c1 FROM ("S 1"."T 4" r2 INNER JOIN unnest('{3,6,9,12,15,18}'::integer[]) f3(c1) ON (((r2.c1 = f3.c1))))
(10 rows)
-- The remaining scenarios reuse a dedicated foreign table to cover the
-- corner cases of FUNCTION RTE push-down: function-first FROM, record

View File

@@ -883,13 +883,13 @@ EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1
FROM ft1 t1, ft6 t2, unnest(ARRAY[3, 6, 9, 12, 15, 18]::int[]) AS u(id)
WHERE t1.c1 = u.id AND t2.c1 = u.id;
-- Selective predicate on ft1.c3 (not in the eqclass) shrinks ft1 to a
-- handful of remote rows; now ft6 is effectively the bigger side and the
-- function is absorbed into ft6 instead.
-- Equality on ft1.c3 (not in the eqclass) restricts ft1 to a single remote row;
-- now ft6 is effectively the bigger side and the function is absorbed into
-- ft6 instead.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1
FROM ft1 t1, ft6 t2, unnest(ARRAY[3, 6, 9, 12, 15, 18]::int[]) AS u(id)
WHERE t1.c1 = u.id AND t2.c1 = u.id AND t1.c3 < '00010';
WHERE t1.c1 = u.id AND t2.c1 = u.id AND t1.c3 = '00009';
-- The remaining scenarios reuse a dedicated foreign table to cover the
-- corner cases of FUNCTION RTE push-down: function-first FROM, record