Fix Memoize to work with partitionwise joining.

A couple of places weren't up to speed for this.  By sheer good
luck, we didn't fail but just selected a non-memoized join plan,
at least in the test case we have.  Nonetheless, it's a bug,
and I'm not quite sure that it couldn't have worse consequences
in other examples.  So back-patch to v14 where Memoize came in.

Richard Guo

Discussion: https://postgr.es/m/CAMbWs48GkNom272sfp0-WeD6_0HSR19BJ4H1c9ZKSfbVnJsvRg@mail.gmail.com
This commit is contained in:
Tom Lane 2022-12-05 12:36:41 -05:00
parent 1bd84ef677
commit dc3648f65b
5 changed files with 74 additions and 3 deletions

View File

@ -490,6 +490,7 @@ get_memoize_path(PlannerInfo *root, RelOptInfo *innerrel,
Path *outer_path, JoinType jointype,
JoinPathExtraData *extra)
{
RelOptInfo *top_outerrel;
List *param_exprs;
List *hash_operators;
ListCell *lc;
@ -579,10 +580,21 @@ get_memoize_path(PlannerInfo *root, RelOptInfo *innerrel,
return NULL;
}
/*
* When considering a partitionwise join, we have clauses that reference
* the outerrel's top parent not outerrel itself.
*/
if (outerrel->reloptkind == RELOPT_OTHER_MEMBER_REL)
top_outerrel = find_base_rel(root, bms_singleton_member(outerrel->top_parent_relids));
else if (outerrel->reloptkind == RELOPT_OTHER_JOINREL)
top_outerrel = find_join_rel(root, outerrel->top_parent_relids);
else
top_outerrel = outerrel;
/* Check if we have hash ops for each parameter to the path */
if (paraminfo_get_equal_hashops(root,
inner_path->param_info,
outerrel,
top_outerrel,
innerrel,
&param_exprs,
&hash_operators,

View File

@ -4176,6 +4176,7 @@ do { \
FLAT_COPY_PATH(mpath, path, MemoizePath);
REPARAMETERIZE_CHILD_PATH(mpath->subpath);
ADJUST_CHILD_ATTRS(mpath->param_exprs);
new_path = (Path *) mpath;
}
break;

View File

@ -1508,8 +1508,8 @@ typedef struct MemoizePath
{
Path path;
Path *subpath; /* outerpath to cache tuples from */
List *hash_operators; /* hash operators for each key */
List *param_exprs; /* cache keys */
List *hash_operators; /* OIDs of hash equality ops for cache keys */
List *param_exprs; /* expressions that are cache keys */
bool singlerow; /* true if the cache entry is to be marked as
* complete after caching the first record. */
bool binary_mode; /* true when cache key should be compared bit

View File

@ -196,6 +196,45 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
(8 rows)
DROP TABLE strtest;
-- Ensure memoize works with partitionwise join
SET enable_partitionwise_join TO on;
CREATE TABLE prt (a int) PARTITION BY RANGE(a);
CREATE TABLE prt_p1 PARTITION OF prt FOR VALUES FROM (0) TO (10);
CREATE TABLE prt_p2 PARTITION OF prt FOR VALUES FROM (10) TO (20);
INSERT INTO prt VALUES (0), (0), (0), (0);
INSERT INTO prt VALUES (10), (10), (10), (10);
CREATE INDEX iprt_p1_a ON prt_p1 (a);
CREATE INDEX iprt_p2_a ON prt_p2 (a);
ANALYZE prt;
SELECT explain_memoize('
SELECT * FROM prt t1 INNER JOIN prt t2 ON t1.a = t2.a;', false);
explain_memoize
------------------------------------------------------------------------------------------
Append (actual rows=32 loops=N)
-> Nested Loop (actual rows=16 loops=N)
-> Index Only Scan using iprt_p1_a on prt_p1 t1_1 (actual rows=4 loops=N)
Heap Fetches: N
-> Memoize (actual rows=4 loops=N)
Cache Key: t1_1.a
Cache Mode: logical
Hits: 3 Misses: 1 Evictions: Zero Overflows: 0 Memory Usage: NkB
-> Index Only Scan using iprt_p1_a on prt_p1 t2_1 (actual rows=4 loops=N)
Index Cond: (a = t1_1.a)
Heap Fetches: N
-> Nested Loop (actual rows=16 loops=N)
-> Index Only Scan using iprt_p2_a on prt_p2 t1_2 (actual rows=4 loops=N)
Heap Fetches: N
-> Memoize (actual rows=4 loops=N)
Cache Key: t1_2.a
Cache Mode: logical
Hits: 3 Misses: 1 Evictions: Zero Overflows: 0 Memory Usage: NkB
-> Index Only Scan using iprt_p2_a on prt_p2 t2_2 (actual rows=4 loops=N)
Index Cond: (a = t1_2.a)
Heap Fetches: N
(21 rows)
DROP TABLE prt;
RESET enable_partitionwise_join;
-- Exercise Memoize code that flushes the cache when a parameter changes which
-- is not part of the cache key.
-- Ensure we get a Memoize plan

View File

@ -103,6 +103,25 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
DROP TABLE strtest;
-- Ensure memoize works with partitionwise join
SET enable_partitionwise_join TO on;
CREATE TABLE prt (a int) PARTITION BY RANGE(a);
CREATE TABLE prt_p1 PARTITION OF prt FOR VALUES FROM (0) TO (10);
CREATE TABLE prt_p2 PARTITION OF prt FOR VALUES FROM (10) TO (20);
INSERT INTO prt VALUES (0), (0), (0), (0);
INSERT INTO prt VALUES (10), (10), (10), (10);
CREATE INDEX iprt_p1_a ON prt_p1 (a);
CREATE INDEX iprt_p2_a ON prt_p2 (a);
ANALYZE prt;
SELECT explain_memoize('
SELECT * FROM prt t1 INNER JOIN prt t2 ON t1.a = t2.a;', false);
DROP TABLE prt;
RESET enable_partitionwise_join;
-- Exercise Memoize code that flushes the cache when a parameter changes which
-- is not part of the cache key.