MDEV-41303: rand() in a semi-join subquery is checked on outer rows - #5799
DaveGosselin-MariaDB wants to merge 1 commit into
Conversation
explain extended
select count(*) from t1
where t1.a in (select c from t2 where rand(1) < 0.09);Before the patch, we get: After the patch, we get (I've added formatting): So it is still converted into a semi-join. |
| 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 80 100.00 | ||
| Warnings: | ||
| Note 1003 select count(0) AS `count(*)` from `test`.`t1` semi join (`test`.`t2`) where rand(1) < 2 | ||
| set optimizer_switch='firstmatch=default'; |
There was a problem hiding this comment.
Does re-running the test with firstmatch enabled add any value?
The first query uses the same query plan.
The second uses First Match, but we've already saw
from `test`.`t1` semi join (`test`.`t2`) where rand(1) < 2
above...
There was a problem hiding this comment.
I'm happy to remove it, but I like the tests to be WYSIWYG as much as possible, so re-running shows the baseline compared against the change.
|
Remember I've asked this question on the call:
Claude gives this answer:
I've verified it for SYSDATE()... |
Yes, I investigated with Claude and discovered that the answer to this is 'no'. I posted this PR because the answers to the three questions we discussed on the call indicated that this solution is sound, so I thought I would post it. I planned to discuss the questions during the team call today. |
Do not merge a subquery into its parent as a semi-join when it has the UNCACHEABLE_RAND flag, which RAND() and ROWNUM set. Derived tables already follow this rule. ROWNUM sets the same flag, so this patch replaces the check for ROWNUM with a check for UNCACHEABLE_RAND. Previously, converting an IN subquery to a semi-join moved its WHERE into the parent WHERE. A condition there such as rand(1) < 0.09 doesn't rely on any columns, so it is attached to the last table of the join order that is outside any materialized semi-join. With SJ-Materialization it was checked once for each outer row instead of once for each row of the subquery, and the query returned a wrong count. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3316dea to
afd7642
Compare
Converting an IN subquery to a semi-join moves its WHERE into the parent WHERE. A condition there such as rand(1) < 0.09 reads no table, so it is attached to the last table of the join order that is outside any materialized semi-join. With SJ-Materialization it was checked once for each outer row instead of once for each row of the subquery, and the query returned a wrong count.
Do not convert a subquery to a semi-join when it contains a function with a random result (UNCACHEABLE_RAND). Derived tables already follow this rule. ROWNUM sets the same flag, so this replaces the check for ROWNUM.