From 6b7c82c4b3792a426ed8968669d4b7e995bba611 Mon Sep 17 00:00:00 2001 From: Venu Kummari Date: Mon, 2 Nov 2026 15:58:47 +0000 Subject: [PATCH] Don't make a SubPlan's output Params Memoize cache keys Commit f8da64e6 makes cache keys of the PARAM_EXEC Params found in the memoized relation's base quals and targetlist. pull_exec_params_walker() also descended into SubPlans, so the Params in a SubPlan's testexpr, which stand for the subplan's own output and are set by the subplan itself, became cache keys too. The Memoize node then referenced a Param that only its child sets, and finalize_plan() failed with "plan should not reference subplan's variable", e.g. for SELECT ... FROM a JOIN b ON a.x = b.x WHERE 5 > ALL (SELECT b.x); with a nested loop and Memoize over b. Only a SubPlan's args are evaluated in the memoized relation's context, so look only at those. --- src/backend/optimizer/path/joinpath.c | 10 ++++++++ src/test/regress/expected/memoize.out | 34 +++++++++++++++++++++++++++ src/test/regress/sql/memoize.sql | 12 ++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index b690deb6..f25b7a22 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -697,6 +697,16 @@ pull_exec_params_walker(Node *node, List **params) *params = lappend(*params, param); return false; } + if (IsA(node, SubPlan)) + { + /* + * The Params in a SubPlan's testexpr stand for the subplan's own + * output, which it sets itself; they are not supplied from above, so + * they must not become cache keys. Only the args are evaluated in our + * context. + */ + return pull_exec_params_walker((Node *) ((SubPlan *) node)->args, params); + } return expression_tree_walker(node, pull_exec_params_walker, params); } diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index 819cdf41..66f4f634 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -677,3 +677,37 @@ SELECT * FROM tab_anti t1 WHERE t1.a IN (10 rows) DROP TABLE tab_anti; +-- A SubPlan in the memoized relation's quals sets the Params of its own +-- output; they must not be made cache keys +SET enable_hashjoin TO off; +SET enable_mergejoin TO off; +EXPLAIN (COSTS OFF) +SELECT COUNT(*) FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.twenty = t2.unique1 +WHERE 5 > ALL (SELECT t2.unique1) AND t1.unique1 < 1000; + QUERY PLAN +------------------------------------------------------------------- + Aggregate + -> Nested Loop + -> Bitmap Heap Scan on tenk1 t1 + Recheck Cond: (unique1 < 1000) + -> Bitmap Index Scan on tenk1_unique1 + Index Cond: (unique1 < 1000) + -> Memoize + Cache Key: t1.twenty + Cache Mode: logical + -> Index Only Scan using tenk1_unique1 on tenk1 t2 + Index Cond: (unique1 = t1.twenty) + Filter: (ALL (5 > (SubPlan all_1).col1)) + SubPlan all_1 + -> Result +(14 rows) + +SELECT COUNT(*) FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.twenty = t2.unique1 +WHERE 5 > ALL (SELECT t2.unique1) AND t1.unique1 < 1000; + count +------- + 250 +(1 row) + +RESET enable_hashjoin; +RESET enable_mergejoin; diff --git a/src/test/regress/sql/memoize.sql b/src/test/regress/sql/memoize.sql index bf7ea5c0..db6ed228 100644 --- a/src/test/regress/sql/memoize.sql +++ b/src/test/regress/sql/memoize.sql @@ -332,3 +332,15 @@ SELECT * FROM tab_anti t1 WHERE t1.a IN (SELECT t1.b FROM tab_anti t3 WHERE t2.a > 1 OFFSET 0)); DROP TABLE tab_anti; + +-- A SubPlan in the memoized relation's quals sets the Params of its own +-- output; they must not be made cache keys +SET enable_hashjoin TO off; +SET enable_mergejoin TO off; +EXPLAIN (COSTS OFF) +SELECT COUNT(*) FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.twenty = t2.unique1 +WHERE 5 > ALL (SELECT t2.unique1) AND t1.unique1 < 1000; +SELECT COUNT(*) FROM tenk1 t1 INNER JOIN tenk1 t2 ON t1.twenty = t2.unique1 +WHERE 5 > ALL (SELECT t2.unique1) AND t1.unique1 < 1000; +RESET enable_hashjoin; +RESET enable_mergejoin; -- 2.43.0