Console View
|
Categories: connectors experimental galera main |
|
| connectors | experimental | galera | main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37563 Crash on a FOR loop with a row comparison in the upper bound Both bounds of a FOR loop, and all parameters of a cursor FOR loop, are parsed before their instructions are added. add_instr() then gave all their items to the first instruction, and in a trigger also all NEW and OLD fields. So a row comparison used by a later instruction was cleaned up by the wrong one, on the next CALL, after its memory had already been freed. This crashed in Arg_comparator::cleanup(). Give each instruction only the items of its own expression, and its own trigger fields. To find them, cut THD::free_list at the newest item of each expression, starting with the oldest expression. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Drop the rowid filter for the FULL JOIN null complement scan The pass that emits the right side rows of a FULL JOIN which never matched reads the right table with a plain sequential scan rather than the key lookup the plan chose. A rowid filter belongs to that lookup because it holds the primary keys the lookup was allowed to return, and an engine is entitled to assume it never sees one outside an index read. InnoDB asserts on !prebuilt->index->is_primary() when it does. The filter is set aside for the length of the scan and put back once the scan is closed, which is what the executor already does where it swaps a key lookup for a table scan while running a subquery. Section 24 covers a plan that reads the right table by key with a filter on a second index. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-31567 SIGSEGV in reinit_stmt_before_use | Prepared_statement::execute If the first execution of a prepared statement was killed (for example by max_statement_time) after the top-level JOIN had done the once-per-statement semi-join rewrite of the nested subqueries, but before the nested selects saved their rewritten WHERE into prep_where, the statement was left half-transformed. A later execution then used a freed Item from prep_where or an unfixed Item_field and crashed. Set LEX::needs_reprepare when JOIN::optimize() fails during the select's first optimization, so the statement is re-prepared before it runs again. Also make the handling of needs_reprepare safe when the re-prepare fails: keep the flag so it is tried again, do not skip a pending long data error, and invalidate the SP instruction so that a failed re-parse is retried. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
DerZc
34330257+DerZc@users.noreply.github.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40762: Wrong MIN()/MAX() in loose index scan Problem: GROUP BY with MIN()/MAX() can return the opposite extremum when loose index scan chooses among indexes with different ordering. MDEV-32732 (ec6f3208837d3e3625761d26555e7899dd2e7005) added support for descending indexes in loose index scan. It exposed a previously harmless incorrect MIN/MAX key-part pointer: index enumeration overwrites min_max_arg_part even when the candidate is rejected or not selected. The pointer can then refer to a different index from the chosen plan. Using its index-specific key_part_flag to determine scan direction causes the regression. Fix: Keep the MIN/MAX key-part pointer local to each candidate and use it for the candidate's gap, infix and predicate checks. Save it with the chosen index only when that candidate becomes the best plan. (Cosmetic edits to testcase and commit comment by Sergei Petrunia) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Record every FULL JOIN match on the right side A FULL JOIN runs as a LEFT JOIN of its left side over its right side, then a pass emits the right side rows that matched no left row. The pass reads a record of which right side rows matched, and a row missing from that record is emitted as a row that matched nothing. The WHERE predicates on the right side are held back by a guard that stays closed until the first match of the current left side row, which is what let the first match be recorded whatever the WHERE then made of the row. For a second or later matching row of the same left row the guard is already open, so a WHERE predicate could reject the row before its match was recorded, and the pass brought it back with the left side all NULL. The match is now recorded for every right side row whose match condition holds. Closing the guard again gives that condition on its own, since the guard withholds exactly what is no part of the match. Section 23 covers a left row with two matching right rows, one of them rejected by the WHERE. Before this change that row came back a second time with the left side NULL, which the UNION of a LEFT JOIN and a RIGHT JOIN does not produce. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Daniel Bartholomew
db@dbart.us |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bump the VERSION | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40760: Stop using common_header_len and post_header_len from FD event The FORMAT_DESCRIPTION_EVENT contains values common_header_len and post_header_len array for individual event types, which were intended to describe the format of following events in the binlog/relaylog. However, this flexibility has been problematic and never useful: - The values have never changed in MariaDB since before 10.0, nor in MySQL. - Having to always have the correct format description event available in the code when reading an event causes a lot of complexity (and has historically caused numerous bugs). - The potential for a corrupt or malicious format description event requires careful validation that these length values are correct, which again has historically seen bugs. Since the values never change anyway, this patch removed the code that reads them when reading the format description event, just using the hard-coded values that are the only valid numbers anyway. Remove support for ancient row events with post_header_len=6. Remove support for reading binlogs created by custom MySQL forks with different common_header_len (ie. MDEV-4645). Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Validate buffer length in Log_event constructors This patch goes through all the constructors of Log_event subclasses that are used to implement read_log_event() and adds error checks on the size of the event buffer to guard against out-of-buffer reads. Adds a lot of checks, and fixes some existing checks that were wrong or in somewhat violation of C++ standard (eg. computing a pointer outside of the available buffer). This removes support for reading Table_map_log_event without metadata; such events were only written by MySQL 5.1 servers going back to 2007. Fix some missing bits in MAX_LOG_EVENT_HEADER, and update test case rpl.rpl_packet with a longer query to still exceed max_allowed_packet. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41666 Corruption on statement rollback when ON UPDATE SET NULL affects the base column Problem: ======= - When a foreign key column is the base column of a virtual column that is indexed together with other virtual columns, a cascaded update (ON UPDATE/DELETE SET NULL) computed the old value of only the dependent virtual column. The other virtual key parts of the index stayed NULL in update->old_vrow. If the statement was rolled back, row_upd_store_v_row() used those NULLs, so row_upd_sec_index_entry() searched the secondary index for a record that did not exist and hit the "Record in index was not found on update" assertion. Solution: ========= dict_mem_fill_vcol_set_for_base_col(): Add every virtual key part of each index that contains the dependent virtual column to dict_foreign_t::v_cols, as dict_mem_fill_vcol_from_v_indexes() already does. row_ins_foreign_fill_virtual() then computes the old values of all of them. Based on mysql/mysql-server@c48fa1e57a8e810f89b43d3e94bcbefa1fd69806 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-XXXXX: Keep the last top level base table search in bounds Attaching a condition to the last table of a join walks back from the end of the join order looking for the last table of the top level plan. The search decremented an unsigned index without a lower bound, so a plan whose last entries all stand for runs of other tables ran the index past zero and the walk read memory before the array. The search now stops at the start of the array. The shape that reaches this is a FULL JOIN with a nested join on both sides, which is rejected until the phase 2 gates come off, so the covering test arrives with the queries that shape allows. The defect itself is older than the FULL JOIN work and does not depend on it. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleg Smirnov
olernov@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-38819 Wrong result of 'Range checked for each record' after pushdown from HAVING When a condition was pushed from HAVING into WHERE, its fields were unwrapped in place. The pushed condition then shared field items with the SELECT list. Creating the GROUP BY temporary table later bound those items to the temporary table. With a 'Range checked for each record' plan, range analysis took the value of the outer table's field from the not yet filled temporary table. It built an impossible range, and the query returned no rows. The pushed condition is now cloned before its fields are unwrapped, so it gets its own field items. Items that cannot be cloned (e.g. subqueries) are still unwrapped in place, to keep the pushdown. Such conditions are unlikely to get a 'Range checked for each record' plan. Cloning can also fail with an error, which the pushdown did not handle properly. It modifies HAVING before all of its steps have succeeded, yet on a failure it emptied the list of pushed conditions and let the query continue, so the conditions already removed from HAVING were not checked at all. pushdown_from_having_into_where() now returns an error status, and the query errors out if the pushdown fails after it may have modified HAVING. Based on a contribution by Omar Gamal (PR #4815). Co-authored-by: OmarGamal10 <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Merge branch 'knielsen_log_event_read_validation_base_11.4' into 11.8 This merges the patch series that fixes a lot of missing validation checks for read_log_event(). The merge includes a few added fixes that are needed in new code introduced in 11.8. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41667 InnoDB: Failing assertion: buf <= (byte*) original_upd_buff + buff_len Problem: ======= An UPDATE on an InnoDB table with several indexed virtual columns could corrupt the heap in calc_row_difference() because the write pointer went past the end of the update buffer. ha_innobase::update_row() sized the buffer from the row length, the maximum key length and fixed padding. That did not allow for the extra packed values calc_row_difference() stores for indexed virtual columns for both the new value and the old value. Solution: ======== When allocating the update buffer in ha_innobase::update_row(), add pack_length() of every virtual column. The row length already counts each virtual column once, so the buffer can now hold both the new and old packed values. All virtual columns are counted, not only the indexed ones, because the buffer is reused and a column may become indexed after it is allocated. Based on mysql/mysql-server@12655140610674db615547cdf86af1f7cbf563c1 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37563 Crash on a FOR loop with a row comparison in the upper bound Both bounds of a FOR loop, and all parameters of a cursor FOR loop, are parsed before their instructions are added. add_instr() then gave all their items to the first instruction, and in a trigger also all NEW and OLD fields. So a row comparison used by a later instruction was cleaned up by the wrong one, on the next CALL, after its memory had already been freed. This crashed in Arg_comparator::cleanup(). Give each instruction only the items of its own expression, and its own trigger fields. To find them, cut THD::free_list at the newest item of each expression, starting with the oldest expression. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables Alternative, more general fix, Variant 2. The problem was as follows: Suppose we are running a PS/SP statement and we get an error while doing optimization that is done once per statement life. This may leave the statement data structures in an undefined state, where it is not safe to execute it again. The fix: introduce LEX::needs_reprepare and set it in such cases. Make PS and SP runtime check it and re-prepare the statement before executing it again. We do not use Reprepare_observer, because it turns out it is tightly tied to watching versions of statement's objects. For example, it must not be used when running the statement for the first time, exactly when the once-per-statement-lifetime optimizations are done. (cherry picked from commit 0e21ff8ca47ab637564b4c120322215caa70835d) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-31567 SIGSEGV in reinit_stmt_before_use | Prepared_statement::execute If the first execution of a prepared statement was killed (for example by max_statement_time) after the top-level JOIN had done the once-per-statement semi-join rewrite of the nested subqueries, but before the nested selects saved their rewritten WHERE into prep_where, the statement was left half-transformed. A later execution then used a freed Item from prep_where or an unfixed Item_field and crashed. Set LEX::needs_reprepare when JOIN::optimize() fails during the select's first optimization, so the statement is re-prepared before it runs again. Also make the handling of needs_reprepare safe when the re-prepare fails: keep the flag so it is tried again, do not skip a pending long data error, and invalidate the SP instruction so that a failed re-parse is retried. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41281 Unbounded Recursion in InnoDB FTS Boolean-Mode causes Stack-Overflow fts_ast_visit expr_lst: expr_lst sub_expr wrapped the expression list parsed so far in a newly created FTS_AST_LIST for every sub-expression, so a query such as '(word)' repeated N times built N nested lists. fts_ast_visit() recurses once per FTS_AST_LIST and overflowed the thread stack; the default thread_stack was exhausted at about 2300 repetitions. Append the sub-expression to the list that is already being built, and create a list only when there is none yet, the way the neighbouring expr_lst: expr_lst expr rule has always done. The sub-expressions now become siblings of a single list and the depth no longer grows. This also corrects '+term (subexpr)'. The FTS_EXIST pass used to run to completion in the inner list before the sub-expression was unioned at the outer level, so rows that matched only the sub-expression were returned. Based on mysql/mysql-server@e46e9db484a051db6ffb0abd2b06d3b38ef71a35 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40760: Remove Format_description_log_event::event_type_permutation The only use of this member was removed back in 2013 (!), but some (now dead) code remains that accesses the member. This patch removes the member and the related dead code properly. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Replace backup_phase with extended backup_stages This is based on a commit 281a0c99986824784a66659f2332db3ec362846f by Monty. According to Monty, this: - Galera should now be supported (needs testing) - Enables ddl logging (so we can use it in the future) - Flushes binary logs (we still must add code to copy them) - mdl locks are consistent between mariadb-backup and backup command - Give errors if backup command is done under a transaction, global read lock or lock tables. - Retry for MDL_BACKUP_WAIT_DDL (needed for backup.backup_ddl_concurrent_verify) Things to do (in addition to the things in Monty's earlier review): - At backup_stage stage start, force rotate of aria log files. This allows us to copy all old logs without any locks - Copy all transactional tables and old aria logs under BACKUP_START (as - maria-backup does) - Copy the active aria log file under block commit (Only one file as aria log rotation is disabled while backup is running. - Improve speed of copying aria tables by copy files in up to 1M blocks and run checksum on the blocks and only re-read blocks with - wrong checksum. - Copy non transactional files under BACKUP_START. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40760: Remove unused Format_description_log_event * arguments Follow-up patch, remove a lot of Format_description_log_event * function arguments that are no longer needed. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Merge branch 'knielsen_log_event_read_validation_base_10.11' into 11.4 This merges the patch series that fixes a lot of missing validation checks for read_log_event(). The merge includes a few added fixes that are needed in new code introduced in 11.4. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Add optimizer switches for the four join transformations Four switches, all on by default, so behavior does not change. Each one turns off a transformation that simplify_joins performs, which helps narrow down where a wrong result comes from. full_join_rewrite rewriting a FULL JOIN as a one sided outer join when the WHERE clause rejects NULLs on one of its operands. Off keeps the FULL JOIN and forces the null complement pass to run. outer_join_to_inner converting an outer join to an inner join when a conjunctive predicate rejects NULLs for one of its inner tables. flatten_join_nests replacing a join nest that carries no ON expression with its children. Off keeps the nesting the parser produced, which costs plan quality without changing results. simplify_joins an umbrella over the other three. Each decision moved out of the code that carries it out, so a switch has one place to read. classify_full_join already held the whole FULL JOIN decision. The outer join conversion shared a block with work that runs for a plain inner join as well, so that decision moved to classify_outer_join, and taking the dependencies of the enclosing nest, moving the ON expression into the WHERE clause, and dropping the outer join marks now happen both for a converted join and for one that was already inner. Registering a semi-join nest moved to register_semijoin_nest, which always runs since it is the only producer of the semi-join nest list, leaving the flattening test on its own. Gating the call to simplify_joins rather than its three decisions would give wrong results, since that pass is the only producer of the nest attributes and table dependencies the rest of the optimizer reads. join_transform_enabled holds the umbrella relationship in one place. subselect_exists2in turns every switch off by rewriting the whole optimizer_switch string, so it now turns flatten_join_nests off as well, and the nest it leaves in place shows up in one line of that test's expected output. One switch list result file also gained reorder_outer_joins, which had been left out when that switch was added. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41281 Unbounded Recursion in InnoDB FTS Boolean-Mode causes Stack-Overflow fts_ast_visit expr_lst: expr_lst sub_expr wrapped the expression list parsed so far in a newly created FTS_AST_LIST for every sub-expression, so a query such as '(word)' repeated N times built N nested lists. fts_ast_visit() recurses once per FTS_AST_LIST and overflowed the thread stack; the default thread_stack was exhausted at about 2300 repetitions. Append the sub-expression to the list that is already being built, and create a list only when there is none yet, the way the neighbouring expr_lst: expr_lst expr rule has always done. The sub-expressions now become siblings of a single list and the depth no longer grows. This also corrects '+term (subexpr)'. The FTS_EXIST pass used to run to completion in the inner list before the sub-expression was unioned at the outer level, so rows that matched only the sub-expression were returned. Based on mysql/mysql-server@e46e9db484a051db6ffb0abd2b06d3b38ef71a35 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37563 Crash on a FOR loop with a row comparison in the upper bound Both bounds of a FOR loop, and all parameters of a cursor FOR loop, are parsed before their instructions are added. add_instr() then gave all their items to the first instruction, and in a trigger also all NEW and OLD fields. So a row comparison used by a later instruction was cleaned up by the wrong one, on the next CALL, after its memory had already been freed. This crashed in Arg_comparator::cleanup(). Give each instruction only the items of its own expression, and its own trigger fields. To find them, cut THD::free_list at the newest item of each expression, starting with the oldest expression. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Allow a nested join as a FULL JOIN operand Phase 2 rejected two shapes. A FULL JOIN whose right operand was a nested join raised ER_FULL_JOIN_BASE_TABLES_ONLY, and a FULL JOIN on the inner side of an enclosing LEFT or RIGHT JOIN raised ER_FULL_JOIN_NOT_ALLOWED_IN_OUTER_JOIN. Both shapes are allowed now and neither error is raised any more. A FULL JOIN emits the rows of one side that never matched with the other side null complemented. An enclosing join's condition has to see those rows complete, which it cannot do while the operand is only a set of tables spread through the join order. An operand that is a nested join is therefore computed into a temporary table before the enclosing join runs, so one JOIN_TAB stands for the whole operand and carries the FULL JOIN's own marks. The chosen plan has to keep such an operand on an unbroken span of the join order, and that span becomes a run of JOIN_TABs of its own, the way a materialized semi join already is. Nothing outside the operand is positioned while the operand is computed, so a table inside it can carry no condition that names an outside table, an equality class that spans the boundary is split at it, and the outer join scope of a join outside it stops there. A nest goes on naming a table that table elimination removed, and an eliminated table is a const table, so the test for entering the run leaves the eliminated tables out. The test for entering a materialized semi join run reads the same way. The operand keeps a copy of its own ON expression, since simplifying the join tree merges an inner join's ON into the enclosing outer join's ON, which is not equivalent for a FULL JOIN because it changes which rows of the right side never match. An operand can itself be built from more than one join, so an ON expression can sit a level or two below the top of it, and every such expression is recorded on the operand as a whole, which is the nest that reads them back. A predicate over the operand is checked again at the entry standing for it, once the null complementing has happened. A FULL JOIN's left side WHERE predicates are deferred to the table that completes its right side even when that side is a nest. The enclosing ON contributes no access path for the operand. Reading a row back restores what a nest table cannot hold on its own, whether the row was null complemented for a column declared NOT NULL, and the status that says whether the table holds a usable row at all. The JOIN_TAB standing for a run covers every table of the nest, the eliminated ones included. The plan output therefore skips only a JOIN_TAB that is a table of the query, since a JOIN_TAB standing for a run is never eliminated. The FULL JOIN code moves out of sql_select.cc, sql_base.cc and table.cc into opt_full_join.cc and opt_full_join.h. JOIN::full_joins holds a Full_join_info, declared in opt_full_join_info.h, for each FULL JOIN that simplify_joins did not rewrite. It holds the two sides, the WHERE conjuncts held back for the FULL JOIN, the tab that reads its right side, the duplicate filter of the right rows that matched, and the next FULL JOIN whose null complement pass runs from the same tab. It replaces fj_dups, fj_first_target and fj_next_target on JOIN_TAB and fj_left_cond on TABLE_LIST. The list is built for each JOIN, so a prepared statement starts every execution with nothing held. opt_join_runs.cc and opt_join_runs.h hold the runs. get_best_combination records every run of the chosen join order in a Join_run_layout before it writes any JOIN_TAB, and the layout gives the exact number of top level JOIN_TABs. make_join_select walks the plan with Join_tab_walk, which keeps the tables a run sets aside on entry until the walk reaches the run's placeholder. A FULL JOIN run can be inside a materialized semi join, so the walk keeps the runs on a stack. make_join_select decides again which conditions wait for a FULL JOIN's right partner, on each condition about to be attached to a table. Distributing the WHERE builds conditions for one table out of disjunctions that span both sides, and a HAVING predicate moved into the WHERE can name the left side alone, so the choice made while the WHERE is rewritten misses both. That earlier choice stays, since it keeps the left side out of ref and range analysis. Whether a condition reads a table after the partner is decided from the join order positions of the tables it reads. An enclosing join's ON supplies no key value for a table on a FULL JOIN's left side, because the null complement pass is complete only once every left row has been read. A key value that reads only tables of the same left side is still allowed. An ORDER BY over a FULL JOIN that simplify_joins kept sorts through a temporary table, because the null complement pass emits right rows after the scan of the first table. EXPLAIN gathers the tables of each run at its own depth, and an operand nest inside a materialized semi join reports under that semi join's id. Sections 22 to 28 cover the new shapes. The section covering more than one matching right side row becomes Section 28, which adds a nest operand to the same case, and the two sections that came with the fixes before this one are renumbered to follow it. Sections 35 to 42 cover an ON expression below the top of an operand, an enclosing ON that keeps the left side NULLs, operand nests that table elimination shrinks, a condition offered to a table inside a run, a prepared WHERE that folds away on a later execution, a FULL JOIN run inside a materialized semi join, and a left side condition over a table before the partner. Sections 8, 18 and 27 gain left side predicates that appear only once the WHERE is rewritten. The helper that gives every column an index of its own comes with them, ahead of the axis that turns it on. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Run the FULL JOIN tests over more engines and switches full_join.test runs once for every point of three crossed axes, the optimizer switch axis in full_join.combinations, the storage engine axis in full_join_engine.combinations and the index axis in full_join_index.combinations, thirty runs in all. The result file in the tree is the run with the default engine, no added indexes and the switches at their defaults. Every other run's expected output is a diff against it, so a run whose output matches it has no file of its own, and a query added to the test shows up in all thirty runs. The index axis gives every column of every base table a non-unique index on itself. The work is decided by what the database holds at the point full_join_add_indexes.inc is sourced, so a section sources it after the tables it creates. A non-unique index is the only kind that can be added without knowing the data, since a primary key or a unique index would fail on the duplicates and the NULLs that many sections put in a join column on purpose. The switch axis holds the four switches at their defaults and each of them off in turn, which is what makes a wrong result attributable to one transformation. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alessandro Vetere
iminelink@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40123 Assertion failure after lock wait is unblocked in SKIP LOCKED A locking read with SKIP LOCKED through a secondary index calls row_sel_get_clust_rec_for_mysql() to lock the clustered index record. On a lock conflict, that function returned DB_LOCK_WAIT without setting the output record, and row_search_mvcc() then called lock_trx_handle_wait() to cancel the wait. If the conflicting transaction had committed in the meantime, the waiting lock was already granted and lock_trx_handle_wait() returned DB_SUCCESS. row_search_mvcc() then found clust_rec == NULL for a locking read: debug builds failed an assertion, and release builds skipped a row whose lock the transaction now held. Row_sel_get_clust_rec_for_mysql::operator()(): Call lock_trx_handle_wait() here instead of in row_search_mvcc(). When the lock was granted meanwhile, return the clustered index record and store the cursor position as for any granted lock, like row_search_mvcc() does with "goto locks_ok" for the record that it is positioned on. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36057: Assertion failure on 2nd execution of parameterized PS Followup to fix for MDEV-35318: In Prepared_statement::execute_loop(), if we enter this function with lex->needs_reprepare=true, we need to re-prepare the statement. We also need to call set_parameters() to get PS parameter values. We failed to do this and that's the bug. Note that we need to also handle the other reprepare scenario: when we get into Prepared_statement::execute_loop(), call set_parameters() and then hit a "DDL was changed" error with reprepare_observer, we should NOT call set_parameters() again. The parameters have already been transferred to the reprepared statement in Prepared_statement::reprepare()|swap_parameter_array(). (cherry picked from commit 80067a69feaeb5df30abb1bfaf7d4e713ccbf027) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40620: Null complement a merged derived table inside a FULL JOIN A FULL JOIN's null complement pass marks the whole opposite operand as a null row before it emits a row of the side it rescans. The walk that marks the operand treated a TABLE_LIST as either a plain table or a nest. A merged derived table or view is both, its own placeholder and the tables it was merged into, so the walk marked the placeholder and stopped. The query reads the fields of the merged tables, which still held the values of an earlier row, and those values reached the client where NULL belonged. The walk now descends into the nested tables as well as marking the placeholder. Section 22 covers the shape with the flattening of join nests turned off, which leaves the derived table in a nest of its own so the walk reaches it. Before this change the FULL JOIN returned the left side value 3 against the right side rows 3 and 4, where the UNION of a LEFT JOIN and a RIGHT JOIN returns NULL. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-XXXXX: Keep an ON equality that a FULL JOIN left side reads by key A ref access guarantees an equality only for the rows it read, so the optimizer normally drops that equality from the outer join's ON condition. A table on the left side of a surviving FULL JOIN also reaches the ON condition as a null row, produced by the pass that emits the right side rows which never matched on the left, and the ON condition has to reject those rows. With the equality gone, nothing did, and a row the outer join owed as a null complement was lost. The equality now stays in the condition for such a table. The existing deferral machinery moves it onto the FULL JOIN's right side under the found match guard, so it is checked once the match has been recorded. The shape that reaches this is a FULL JOIN whose right side is a nested join, which is rejected until the phase 2 gates come off, so the covering test arrives with the queries that shape allows. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-XXXXX: Save and restore the status of tables inside a run Walking back over the tables a join buffer scan feeds from takes a copy of each table's status and clears it, then puts the copy back when the scan closes. For a table standing for a run of other tables, the tables in that run were left out of the copy but were still written to when the copy was put back, so they received a value the copy had never taken. They now take part in both directions. The work for one table moved into a function that reaches the tables of any run it stands for, however deeply those runs nest, which is what the rest of the join order walking already does. This is a defect in the handling of a materialized semi join, which is the only kind of run a released version produces, so it is not specific to FULL JOIN. No query is known to reach it today. The FULL JOIN work that follows produces runs that nest, which reaches it. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36080: Assertion on 2nd PS execution with error and Array Binding The fix for MDEV-35318 has introduced LEX::needs_reprepare and logic in Prepared_statement::execute_loop() and sp_lex_keeper:: validate_lex_and_exec_core() to re-prepare the statement if it has hit an error when doing once-per-statement-life optimizations. But there is also third code path: PS with Array Binding is handled in Prepared_statement::execute_bulk_loop(). Add handling there as well. (cherry picked from commit 33e0796e7a154e02a5e53c55cefc5d6feb4f5e6d) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables Alternative, more general fix, Variant 2. The problem was as follows: Suppose we are running a PS/SP statement and we get an error while doing optimization that is done once per statement life. This may leave the statement data structures in an undefined state, where it is not safe to execute it again. The fix: introduce LEX::needs_reprepare and set it in such cases. Make PS and SP runtime check it and re-prepare the statement before executing it again. We do not use Reprepare_observer, because it turns out it is tightly tied to watching versions of statement's objects. For example, it must not be used when running the statement for the first time, exactly when the once-per-statement-lifetime optimizations are done. (cherry picked from commit 0e21ff8ca47ab637564b4c120322215caa70835d) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36057: Assertion failure on 2nd execution of parameterized PS Followup to fix for MDEV-35318: In Prepared_statement::execute_loop(), if we enter this function with lex->needs_reprepare=true, we need to re-prepare the statement. We also need to call set_parameters() to get PS parameter values. We failed to do this and that's the bug. Note that we need to also handle the other reprepare scenario: when we get into Prepared_statement::execute_loop(), call set_parameters() and then hit a "DDL was changed" error with reprepare_observer, we should NOT call set_parameters() again. The parameters have already been transferred to the reprepared statement in Prepared_statement::reprepare()|swap_parameter_array(). (cherry picked from commit 80067a69feaeb5df30abb1bfaf7d4e713ccbf027) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alessandro Vetere
iminelink@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41694 Read view after DDL returns wrong data when the table definition was reloaded dict_index_t::trx_id exists only in memory. After a table was evicted from the InnoDB data dictionary cache and loaded again, it was 0, so an old read view could use indexes whose data was replaced by DDL. The old read view then got wrong data after ALTER TABLE ... ALGORITHM=COPY, a table rebuild, ADD INDEX, TRUNCATE TABLE and IMPORT TABLESPACE, instead of ER_TABLE_DEF_CHANGED. dict_load_indexes(): Restore dict_index_t::trx_id from the DB_TRX_ID of the SYS_INDEXES record, except for a discarded tablespace, which cannot be read anyway. RENAME INDEX also updates the record, so after a reload an old read view cannot use a renamed index either. innodb.mvcc_ddl_evict runs a matrix of DDL statements against an old read view, first without and then with eviction of the table from the InnoDB data dictionary cache. Assisted-by: Claude:claude-opus-5-5 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fixup! 92d9175305639ea3e0e9d817540fa3d1d1ebde82 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40745: Validate field parsing of optional_metadata Protect accesses into the m_optional_metadata buffer to not access outside of the buffer in case of corrupt/malicious event data. Protect against buffer overflow of the m_column_name array in case of excessive column names in the event data. Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36080: Assertion on 2nd PS execution with error and Array Binding The fix for MDEV-35318 has introduced LEX::needs_reprepare and logic in Prepared_statement::execute_loop() and sp_lex_keeper:: validate_lex_and_exec_core() to re-prepare the statement if it has hit an error when doing once-per-statement-life optimizations. But there is also third code path: PS with Array Binding is handled in Prepared_statement::execute_bulk_loop(). Add handling there as well. (cherry picked from commit 33e0796e7a154e02a5e53c55cefc5d6feb4f5e6d) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||