Re: Use merge-based matching for MCVs in eqjoinsel

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Ilia Evdokimov <ilya(dot)evdokimov(at)tantorlabs(dot)com>
Cc: David Geier <geidav(dot)pg(at)gmail(dot)com>, pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: Use merge-based matching for MCVs in eqjoinsel
Date: 2025-11-17 18:30:13
Message-ID: 841773.1763404213@sss.pgh.pa.us
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Ilia Evdokimov <ilya(dot)evdokimov(at)tantorlabs(dot)com> writes:
> On 14.11.2025 01:21, Tom Lane wrote:
>> So after a bit of hacking I ended up with the attached. I split up
>> the refactorization into several steps to make it easier to review.

> I reviewed patches 0002-0004 with the refactoring, and I think the
> overall approach is excellent. However, I noticed one issue: in
> eqjoinsel_semi() the variable 'nmatches' is not initialized and can lead
> to undefined behavior when clamped_nvalues2 == sslot2->nvalues. Before
> the refactoring it was initialized by zero.

Argh! Can't believe I missed that.

I experimented with combining all of eqjoinsel_find_matches' outputs
into one struct, but decided that that was uglier than just adding one
more pass-by-reference argument to eqjoinsel_inner/semi.

>> There are still a couple of XXX comments in the attached, denoting
>> loose ends to look at.

In the attached v6, I cleaned up one of the XXX items, deciding that
duplicate entries in the hash table should be coped with not asserted
against. The reason is that we might be working with a comparison
operator that is not exactly the one used to build the MCV list:
the planner is usually pretty cavalier about applying stats that are
only fuzzy matches to what it needs. So it seems possible that we
could find entries that are equal according to the operator we're
using, even though they're unequal according to what ANALYZE used to
compute the MCV list. I didn't actively try to hit that Assert, but
I think a counterexample could be built by using a case-insensitive
collation in a join query.

>> In particular, I wondered whether the
>> hash threshold check
>> if (Min(sslot1.nvalues, sslot2.nvalues) >= EQJOINSEL_MCV_HASH_THRESHOLD)
>> should use Max() instead --- that is, it might be safer to hash
>> if either MCV list is long. Or, holding one's head at a different
>> angle, perhaps the sum of the list lengths should be what's checked?

> Hmm… using the sum actually seems like a good idea for me.

Actually, after sleeping on it it seems like the obvious thing is
to test "sslot1.nvalues * sslot2.nvalues", since the work we are
thinking about saving scales as that product. But I'm not sure
what threshold value to use if we do that. Maybe around 10000?

regards, tom lane

Attachment Content-Type Size
v6-0001-Add-some-test-scaffolding-to-join_selectivity.patch text/x-diff 2.4 KB
v6-0002-Factor-out-duplicative-code-in-eqjoinsel_inner-eq.patch text/x-diff 8.9 KB
v6-0003-Rethink-eqjoinsel-s-handling-of-reversed-joins.patch text/x-diff 6.3 KB
v6-0004-Share-more-work-between-eqjoinsel_inner-and-eqjoi.patch text/x-diff 12.8 KB
v6-0005-Use-hashing-to-avoid-O-N-2-matching-work-in-eqjoi.patch text/x-diff 14.6 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Sami Imseih 2025-11-17 18:35:46 Re: Report oldest xmin source when autovacuum cannot remove tuples
Previous Message Jacob Champion 2025-11-17 18:13:30 Re: Updating IPC::Run in CI?