java-topology/defects/mysql/patch/mysql-0003-setup-fields-find.md

57 lines
1.8 KiB
Markdown

# UNDF: UNDF-2026-000000177
# mysql-0003 — `setup_fields()`: O(F²) std::find iterator recovery inside item loop
## Status
PATCHED
## Severity
MEDIUM (conditional path — only triggers when split_sum_func fires; bounded in
practice to aggregate-heavy queries with wide SELECT lists)
## Location
`sql/sql_base.cc`, function `setup_fields()`, line ~9496
## Description
`setup_fields` iterates over the `fields` deque (size F) to fix and resolve
each Item. After calling `item->split_sum_func(...)`, the deque may grow
(new items appended), invalidating the range-based iterator `it`. The current
recovery code is:
```cpp
if (old_size != fields->size()) {
it = std::find(fields->begin(), fields->end(), item);
}
```
`std::find` performs an O(F) linear scan to rediscover the current item's
position. In a query with F columns where every column has an aggregate that
triggers `split_sum_func`, this recovery fires F times, each scanning O(F)
elements — O(F²) total.
## Fix
Replace the `std::find` recovery with an index-based approach: replace the
range-for with an explicit index loop. After `split_sum_func` fires, the new
items are always appended to the end, so the current item's physical position
is unchanged — we only need to update the end sentinel. With an index loop,
no re-scan is necessary.
```cpp
// Replace:
for (auto it = fields->begin(); it != fields->end(); ++it) {
...
if (old_size != fields->size()) {
it = std::find(fields->begin(), fields->end(), item); // O(F)
}
}
// With:
for (size_t idx = 0; idx < fields->size(); ++idx) {
Item *item = (*fields)[idx];
// split_sum_func may append to fields; idx stays valid because
// mem_root_deque is stable for existing indices after push_back.
// No re-scan needed.
}
```
## Patch file
See `mysql-0003-setup-fields-find.patch`