Re: Enable partitionwise join for partition keys wrapped by RelabelType

From: Jan Nidzwetzki <jan(at)planetscale(dot)com>
To: Matheus Alcantara <matheusssilv97(at)gmail(dot)com>, jian he <jian(dot)universality(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Enable partitionwise join for partition keys wrapped by RelabelType
Date: 2026-08-28 13:42:15
Message-ID: 7e16122a-36e7-475d-bf78-42090b919fbd@planetscale.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello Matheus,

On 27.08.26 19:08, Matheus Alcantara wrote:
> Thanks for the review. v5 is attached, now split into two patches: 0001 is
> the partprune fix and 0002 is the exprs_known_equal() fix.

Thank you for the updated, split version 5 of the patch. Both patches
apply cleanly to the main branch (de56594), and I verified that only the
added tests fail when the change is not applied. So, the split and the
tests look good to me. I also appreciate the tests added to
'collate.icu.utf8.sql'.

While testing the patch, I noticed an asymmetry between inner and outer
joins. The patch handles the inner join case, while the outer join case
produces a non-partitionwise query plan.

From my understanding, outer-join clauses don't form an equivalence
class. So, have_partkey_equi_join() can only prove the keys equal by
matching the join clause through match_expr_to_partition_keys(), which
strips the clause operand but compares it against rel->partexprs unstripped.

This can be reproduced as follows:

CREATE TABLE e1 (a int, c varchar(40)) PARTITION BY HASH ((c::text));
CREATE TABLE e1_p1 PARTITION OF e1 FOR VALUES WITH (MODULUS 2, REMAINDER 0);
CREATE TABLE e1_p2 PARTITION OF e1 FOR VALUES WITH (MODULUS 2, REMAINDER 1);
CREATE TABLE e2 (a int, c varchar(40)) PARTITION BY HASH ((c::text));
CREATE TABLE e2_p1 PARTITION OF e2 FOR VALUES WITH (MODULUS 2, REMAINDER 0);
CREATE TABLE e2_p2 PARTITION OF e2 FOR VALUES WITH (MODULUS 2, REMAINDER 1);

SET max_parallel_workers_per_gather = 0;
SET enable_hashjoin = off;
SET enable_mergejoin = off;
SET enable_partitionwise_join = on;

-- inner join: partitionwise
jan=# EXPLAIN (COSTS OFF) SELECT * FROM e1 JOIN e2 ON e1.c::text =
e2.c::text;
QUERY PLAN
--------------------------------------------------------
Append
-> Nested Loop
Join Filter: ((e1_1.c)::text = (e2_1.c)::text)
-> Seq Scan on e1_p1 e1_1
-> Materialize
-> Seq Scan on e2_p1 e2_1
-> Nested Loop
Join Filter: ((e1_2.c)::text = (e2_2.c)::text)
-> Seq Scan on e1_p2 e1_2
-> Materialize
-> Seq Scan on e2_p2 e2_2
(11 rows)

-- outer join: not partitionwise
jan=# EXPLAIN (COSTS OFF) SELECT * FROM e1 LEFT JOIN e2 ON e1.c::text =
e2.c::text;
QUERY PLAN
----------------------------------------------
Nested Loop Left Join
Join Filter: ((e1.c)::text = (e2.c)::text)
-> Append
-> Seq Scan on e1_p1 e1_1
-> Seq Scan on e1_p2 e1_2
-> Materialize
-> Append
-> Seq Scan on e2_p1 e2_1
-> Seq Scan on e2_p2 e2_2
(9 rows)

I did a few tests, and I think we need to do the stripping in
match_expr_to_partition_keys() as well. By applying this change, the
query plan changes as follows:

-- Now partitionwise
jan=# EXPLAIN (COSTS OFF) SELECT * FROM e1 LEFT JOIN e2 ON e1.c::text =
e2.c::text;
QUERY PLAN
--------------------------------------------------------
Append
-> Nested Loop Left Join
Join Filter: ((e1_1.c)::text = (e2_1.c)::text)
-> Seq Scan on e1_p1 e1_1
-> Materialize
-> Seq Scan on e2_p1 e2_1
-> Nested Loop Left Join
Join Filter: ((e1_2.c)::text = (e2_2.c)::text)
-> Seq Scan on e1_p2 e1_2
-> Materialize
-> Seq Scan on e2_p2 e2_2
(11 rows)

I drafted a possible fix in my tree, added a test case that fails
without the modification, and exported it as a new patch. Attached is a
new series. Patches 1 and 2 are yours in an unmodified form. Patch 3 is
the new one, which could potentially be squashed into patch 2 if you
find the modification useful.

Best regards
Jan

--
Jan Nidzwetzki
PlanetScale Postgres Core Team

Attachment Content-Type Size
v6-0001-Fix-partition-pruning-for-partition-keys-wrapped-.patch text/plain 6.5 KB
v6-0002-Enable-partitionwise-join-for-partition-keys-wrap.patch text/plain 20.8 KB
v6-0003-Enable-partitionwise-join-for-outer-joins-on-Rela.patch text/plain 6.4 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2026-08-28 13:43:10 Re: remove_useless_joins vs. bug #19560
Previous Message Laurenz Albe 2026-08-28 13:36:31 Re: Adding a stored generated column without long-lived locks