MDEV-41303: rand() in a semi-join subquery is checked on outer rows - #5799
DaveGosselin-MariaDB wants to merge 1 commit into
Conversation
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. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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...
| set optimizer_switch='firstmatch=off'; | ||
|
|
||
| # The subquery must check rand(1) < 0.09 on the 80 rows of t2. Checking | ||
| # it on the 4 rows of t1 instead rejects every row. |
There was a problem hiding this comment.
Please use --echo # so that this shows up in .result too.
the last line should be something that is clear about the expected output, like
-echo # Must produce count(*)=1, not 0.
| # it on the 4 rows of t1 instead rejects every row. | ||
| select count(*) from t1 | ||
| where t1.a in (select c from t2 where rand(1) < 0.09); | ||
| explain extended |
There was a problem hiding this comment.
Same here
--echo # Warning must have "<materialize>(...) JOIN..." and not "`test`.`t1` semi join (`test`.`t2`)"
|
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. |
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.