MDEV-26940 Item_cond::remove_eq_conds leaves corrupt Item_equal#5360
MDEV-26940 Item_cond::remove_eq_conds leaves corrupt Item_equal#5360mariadb-RexJohnston wants to merge 1 commit into
Conversation
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
pushdown_cond_for_derived/merge_into_list/remove_eq_conds leaves an Item_equal in an invalid state. This Item_equal is later used by find_producing_item causing an assert in debug builds and perhaps incorrect results in a release build. This Item_equal should no longer be referred to, so rather than correct the Item_equal, we correct the reference used later to look up our base table field. Affected queries will likely have an outer condition pushed down into 2 different derived tables based on the same base table.
86e2a73 to
8038295
Compare
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
Inline are my review comments. Here are some comments from Claude as well:
Orphan left corrupt after merge — sql/item_cmpfunc.cc:7046 · correctness · PLAUSIBLE
merge() repoints only members still in item->equal_items, leaving item itself with with_const==TRUE over a non-const head. It misses (a) fields fi.remove() stripped from the merged-in equality before merge() ran (item_cmpfunc.cc:7109, which ran once in this test), and (b) a popped const that is a const-table field. Either can trip the new assert (debug) or mis-substitute (release) on other queries. Not reproduced by the committed test.
Unconditional set_item_equal may steal upper-level back-pointers — sql/item_cmpfunc.cc:7056 · correctness · PLAUSIBLE (low confidence)
On the direct-merge path (check_simple_equality, sql_select.cc:16943 — untested here), copied equalities share member Item objects; repointing them unconditionally can redirect a field's item_equal away from the upper-level original, risking wrong results. No concrete repro.
| inline Item* get_const() { return with_const ? equal_items.head() : NULL; } | ||
| inline Item* get_const() | ||
| { | ||
| DBUG_ASSERT(!with_const || equal_items.head()->const_item()); |
There was a problem hiding this comment.
The original implementation implied that equal_items.head() could return NULL. Is it safe to assume, in the assertion, that equal_items.head() is always non-NULL when with_const is true? I suppose that if the equal_items.head() returns NULL, then we will have an assertion-like behavior in that the null pointer dereference will crash, but that's probably not intended.
Should the assertion instead be something like:
DBUG_ASSERT(!with_const || (equal_items.head() && equal_items.head()->const_item()));
There was a problem hiding this comment.
I don't think we could ever get an empty equal_items List in our Item_equal object. Even one one item doesn't make any sense.
| abandoned and left in an invalid state. Repoint their item_equal | ||
| back-pointers at 'this' so nothing follows them to the orphan. | ||
| */ | ||
| List_iterator<Item> li(item->equal_items); |
There was a problem hiding this comment.
You should be able to use List_iterator_fast here instead because you're walking the list and updating elements in the list but not mutating the list itself. List_iterator_fast avoids some extra bookkeeping on the presumption that you're not altering the list itself.
There was a problem hiding this comment.
nice catch, thanks.
As there is only one Aside: multiple Item_equal's containing the same item is exactly what our test case contains, and it's the merge process itself that should leave only one (or zero) instances of Item_equal behind. |
pushdown_cond_for_derived/merge_into_list/remove_eq_conds leaves an Item_equal in an invalid state. This Item_equal is later used by find_producing_item causing an assert in debug builds and perhaps incorrect results in a release build.
Affected queries will likely have an outer condition pushed down into 2 different derived tables based on the same base table.