MDEV-36059: 2nd PS exec crash w/nested VIEWs#5248
Conversation
There was a problem hiding this comment.
Code Review
This pull request addresses a crash during the second execution of a prepared statement with nested views (MDEV-36059) by updating the skipping logic in mysql_handle_derived during the DT_MERGE_FOR_INSERT phase. The reviewer pointed out that checking only the immediate cursor->embedding is insufficient if the view is nested inside another join nest within a semi-join, and suggested traversing up the embedding chain to properly detect the semi-join nest.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
A prepared statement runs preparation again on every execution, and preparation merges any VIEWs named in the statement. A VIEW that appears only inside a subquery is meant to be skipped by the merge for insert pass during mysql_handle_derived, since it is not the target of the DELETE. The first execution prepares with the subquery still nested, so the view is correctly skipped, and the DELETE succeeds. Optimization then converts the IN subquery into a semi-join and adds its tables into the first SELECT_LEX's table list (reassigning them to that SELECT_LEX). This conversion is meant to persist for later executions. Then the PS's second execution prepares against the already flattened VIEW. mysql_handle_derived now finds the subquery's VIEW among the first SELECT_LEX's tables and tries to merge it. mysql_handle_derived already had some existing skip logic to recognize a VIEW, but the semijoin conversion has already merged the VIEW into the first SELECT_LEX, so the skip doesn't correctly apply. So a nested VIEW is merged for insert with no table, causing a crash on the NULL table pointer. Solution is to skip a table during mysql_handle_derived when it is the inner side of a semijoin, recognized by its embedding semijoin nest.
4b91fa5 to
b6a943b
Compare
A prepared statement runs preparation again on every execution, and preparation merges any VIEWs named in the statement. A VIEW that appears only inside a subquery is meant to be skipped by the merge for insert pass during mysql_handle_derived, since it is not the target of the DELETE.
The first execution prepares with the subquery still nested, so the view is correctly skipped, and the DELETE succeeds. Optimization then converts the IN subquery into a semi-join and adds its tables into the first SELECT_LEX's table list (reassigning them to that SELECT_LEX). This conversion is meant to persist for later executions.
Then the PS's second execution prepares against the already flattened VIEW. mysql_handle_derived now finds the subquery's VIEW among the first SELECT_LEX's tables and tries to merge it. mysql_handle_derived already had some existing skip logic to recognize a VIEW, but the semijoin conversion has already merged the VIEW into the first SELECT_LEX, so the skip doesn't correctly apply. So a nested VIEW is merged for insert with no table, causing a crash on the NULL table pointer.
Solution is to skip a table during mysql_handle_derived when it is the inner side of a semijoin, recognized by its embedding semijoin nest.