From 8d6389fd44c8df17e4812d575295ce2370f43c67 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Fri, 20 Oct 2023 17:53:02 +0800 Subject: [PATCH v1] Fix a performance issue with Memoize --- src/backend/optimizer/plan/createplan.c | 13 +++++++--- src/test/regress/expected/memoize.out | 32 +++++++++++++++++++++++++ src/test/regress/sql/memoize.sql | 13 ++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 34ca6d4ac2..4cd0759e55 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -3720,13 +3720,20 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ scan_clauses = extract_actual_clauses(scan_clauses, false); - /* Replace any outer-relation variables with nestloop params */ + /* + * Add the subquery's subplan_params to root->curOuterParams and then + * replace any outer-relation variables with nestloop params + * + * Note that we do them in this order so that we can share the same + * PARAM_EXEC slot for the same Var that both belongs to the subquery's + * uplevel vars and to the NestLoop's outer-relation vars. + */ if (best_path->path.param_info) { - scan_clauses = (List *) - replace_nestloop_params(root, (Node *) scan_clauses); process_subquery_nestloop_params(root, rel->subplan_params); + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); } scan_plan = make_subqueryscan(tlist, diff --git a/src/test/regress/expected/memoize.out b/src/test/regress/expected/memoize.out index f5202430f8..b6b976698c 100644 --- a/src/test/regress/expected/memoize.out +++ b/src/test/regress/expected/memoize.out @@ -92,6 +92,38 @@ WHERE t1.unique1 < 1000; 1000 | 9.5000000000000000 (1 row) +-- Try with LATERAL joins +SELECT explain_memoize(' +SELECT COUNT(*),AVG(t2.t1two) FROM tenk1 t1 LEFT JOIN +LATERAL (SELECT t1.two as t1two, * FROM tenk1 t2 OFFSET 0) t2 +ON t1.two = t2.two +WHERE t1.unique1 < 10;', false); + explain_memoize +------------------------------------------------------------------------------------ + Aggregate (actual rows=1 loops=N) + -> Nested Loop Left Join (actual rows=50000 loops=N) + -> Index Scan using tenk1_unique1 on tenk1 t1 (actual rows=10 loops=N) + Index Cond: (unique1 < 10) + -> Memoize (actual rows=5000 loops=N) + Cache Key: t1.two, t1.two + Cache Mode: binary + Hits: 8 Misses: 2 Evictions: Zero Overflows: 0 Memory Usage: NkB + -> Subquery Scan on t2 (actual rows=5000 loops=N) + Filter: (t1.two = t2.two) + Rows Removed by Filter: 5000 + -> Seq Scan on tenk1 t2_1 (actual rows=10000 loops=N) +(12 rows) + +-- And check we get the expected results. +SELECT COUNT(*),AVG(t2.t1two) FROM tenk1 t1 LEFT JOIN +LATERAL (SELECT t1.two as t1two, * FROM tenk1 t2 OFFSET 0) t2 +ON t1.two = t2.two +WHERE t1.unique1 < 10; + count | avg +-------+------------------------ + 50000 | 0.50000000000000000000 +(1 row) + -- Reduce work_mem and hash_mem_multiplier so that we see some cache evictions SET work_mem TO '64kB'; SET hash_mem_multiplier TO 1.0; diff --git a/src/test/regress/sql/memoize.sql b/src/test/regress/sql/memoize.sql index 29ab1ea62d..b387c162e6 100644 --- a/src/test/regress/sql/memoize.sql +++ b/src/test/regress/sql/memoize.sql @@ -57,6 +57,19 @@ LATERAL (SELECT t2.unique1 FROM tenk1 t2 WHERE t1.twenty = t2.unique1 OFFSET 0) t2 WHERE t1.unique1 < 1000; +-- Try with LATERAL joins +SELECT explain_memoize(' +SELECT COUNT(*),AVG(t2.t1two) FROM tenk1 t1 LEFT JOIN +LATERAL (SELECT t1.two as t1two, * FROM tenk1 t2 OFFSET 0) t2 +ON t1.two = t2.two +WHERE t1.unique1 < 10;', false); + +-- And check we get the expected results. +SELECT COUNT(*),AVG(t2.t1two) FROM tenk1 t1 LEFT JOIN +LATERAL (SELECT t1.two as t1two, * FROM tenk1 t2 OFFSET 0) t2 +ON t1.two = t2.two +WHERE t1.unique1 < 10; + -- Reduce work_mem and hash_mem_multiplier so that we see some cache evictions SET work_mem TO '64kB'; SET hash_mem_multiplier TO 1.0; -- 2.31.0