Console View
|
Categories: connectors experimental galera main |
|
| connectors | experimental | galera | main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| move DuckDB to a separate package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39788: Remove added line in `master.info` format The line-count lines in `master.info` and `relay-log.info` have been inconsistent (off by one) since their introduction. MDEV-37530 “fixed” this with its common code merger by chance, changing `master.info` to use `relay-log.info`’s line-count definition. This change, howëver, affected backward compatibility, as `master.info` now expects an ignored MySQL-only line where the first `key=value` option, `master_use_gtid`, is. Since this legacy text-based format has limitations that make it due for replacement, only code reüsablility is valuable, and its consistency does not outweigh its compatibility. Therefore, this commit solves this problem without reverting code by: * Changing the writing code to be compatible with both interpretations (albeit inconsistent with the reading code) * Adding a shim entry to `master.info`’s list to emulate prior versions’ reading behaviour * Although this solution can only restore upgrade compatibility with versions 10.0+, versions before MariaDB 10 have long been EOL. While here, this commit also fixes code and comments that contradict the actual effect. [P.S.] The test for this regression is pushed to 10.11 in PR #5147. Reviewed-by: Brandon Nesterenko <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Merge branch '11.4' into '11.8' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sutou Kouhei
kou@clear-code.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39556 SIGSEGV in ha_mroonga::storage_set_keys_in_use on SELECT Many pathes exist for Mroonga to open tables without opening indexes. Altering from another storage engine and accessing the information schema statistics is one mechanism. The fix is a backport from upstream. Backport from https://github.com/mroonga/mroonga/commit/0b3541910ede845f6ccb62241a633310f2c27f52#diff-c026dc94c7f79d426e7a68e1e69fdc0f7743296a37d6cd19c9e7117a763f242e Fix a crash bug that may be caused after MySQL/MariaDB upgrade GitHub: fix GH-423 MySQL/MariaDB may open Mroonga tables by "CHECK TABLES" when MySQL/MariaDB is upgraded. Mroonga didn't open indexes when a table is opened by "CHECK TABLES". If "REPAIR TABLE" is needed, a table is reopened. If "REPAIR TABLE" isn't needed, a table is NOT reopened. The table doesn't open indexes. "SELECT ... MATCH AGAINST" for the table causes a crash. Reported by Vincent Pelletier. Thanks!!! Backported to MariaDB by Daniel Black Reviews by: Sutou Kouhei and Oleksandr Byelkin |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| move DuckDB to a separate package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39485 Heap-buffer-overflow upon read in `Rows_log_event` constructor MariaDB recognizes Version 2 Rows Events from MySQL, including the format of the “extra data” field added in this version. (MDEV-5115) When parsing this extra data according to the format, whether this data has sufficient length was only checked by assertions in the `Rows_log_event` constructor and the `mariadb-binlog --verbose` printer. When parsing an event with malformed extra data, these assertions * would straight up terminate the program in debug builds. * were stripped in non-debug (release) builds. This would render the parser defenseless to reading from erroneous memory locations outside of the containing event, which will either crash the program or, for `mariadb-binlog --verbose`, snapshot the running memory to be exposed when outputting the event. This commit replaces those assertions with an actual validity check. Since MariaDB does not generate v2 Rows Events, the included test uses a handcrafted binlog file. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40365 OOB read on malformed `Format_description_log_event` The binary-parsing Format Description Event constructor did not validate content length beyond the superclass `is_valid()` call. When parsing a malformed FDE, to load the content fields added in FDE v4 that are missing in this not-FDE, the parser constructor would read from erroneous memory locations beyond the buffer. If this did not outright crash the program, this would corrupt the FDE. With the Format Description playing a critical role in determining how to parse the events to follow, a corrupted FDE would also corrupt (or trigger a crash in) the parsing of subsequent non-FDE events as well. This commit fills in the validation with a FDE-specific guard. It adds the FDE constant `ST_POST_HEADER_LEN_OFFSET` to assist with comparing to the correct minimum size in the future. (Both of these points are designed to merge with the superclass’s guard as part of the MDEV-30128 merger). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-38722 server crash hp_rec_key_cmp In an extended set operation (select_unit_ext, used when EXCEPT ALL or INTERSECT ALL is present) the HEAP unique index is released once, at the node pointed to by union_distinct, assuming every operation after it is a plain UNION ALL that can be unfolded. That does not hold for a trailing EXCEPT ALL: select_unit_ext::send_data() still calls find_unique_row() for it (only the UNION ALL branch checks is_index_enabled), so it dereferenced the already released index and the server crashed in hp_rec_key_cmp(). optimize_bag_operation() now postpones the index release past such a trailing EXCEPT ALL, so the index is available while it is executed. Keeping the index alive uncovered a second problem. A leading INTERSECT that stays inline in the extended operation (instead of being materialized in a derived table) left the rows that did not match every INTERSECT operand in the temporary table. select_unit::send_eof() removes them for a materialized subsequence, but the select_unit_ext override did not, so once the crash was gone the following operations counted those stale rows and produced a wrong multiset. select_unit_ext::send_eof() already scans the temporary table (to reset the duplicate counter, to fold INTERSECT ALL counters, or to unfold), so the filtering of the non-matching INTERSECT records is done inside those loops (guarded by the filter_intersect flag) and the table is still scanned only once. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
github copilot review instructions Assisted-By: Claude:claude-4.8-opus |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-28730 Remove internal parser usage from InnoDB FTS InnoDB FTS performed all reads and DML on its auxiliary, common and CONFIG tables through the InnoDB internal SQL graph parser. Replace that path with direct B-tree access via a new query-executor layer, and delete the parser-era helpers. row0query.h, row/row0query.cc - new QueryExecutor: - General MVCC-aware record traversal and basic DML on the clustered index. Sits on a btr_pcur and a transaction-owned mtr; record processing goes through a RecordCallback that bundles two std::functions: compare_record() returns SKIP / PROCESS / STOP for a record process_record() handles each PROCESS-ed (MVCC-visible) row Public API: read() scan a clustered index with a search key read_all() full clustered scan (optional start tuple) read_by_index() scan a secondary index, fetch the matching clustered record, deliver it to the callback insert_record() insert a tuple into the clustered index delete_record() delete a row identified by tuple delete_all() delete every row in the clustered index select_for_update() position+X-lock the matching clustered row update_record() update the row select_for_update() locked, falling back to optimistic/pessimistic and external storage paths as needed replace_record() upsert: select_for_update()+update_record(), else insert_record() lock_table(), handle_wait(), commit_mtr() fts0exec.h, fts/fts0exec.cc - new FTSQueryExecutor: Thin wrapper over QueryExecutor specialised for FTS tables. Opens and locks the required tables once and exposes typed helpers keyed by table family. Auxiliary INDEX_[1..6]: open_all_aux_tables() insert_aux_record(aux_index, fts_aux_data_t) delete_aux_record(aux_index, fts_aux_data_t) read_aux() range scan from a given word read_from_range() paginated read that absorbs DB_FTS_EXCEED_RESULT_CACHE_LIMIT internally and resumes from the last word seen Common deletion tables (DELETED, DELETED_CACHE, BEING_DELETED, BEING_DELETED_CACHE): open_all_deletion_tables() insert_common_record(), delete_common_record(), delete_all_common_records(), read_all_common() CONFIG table (<key, value>): open_config_table() / set_config_table() insert_config_record(), update_config_record() (upsert), delete_config_record(), read_config_with_lock() fts_aux_data_t carries the auxiliary row payload. RecordCallback specialisations live alongside the executor: CommonTableReader collects doc_ids from common tables that share the <doc_id> schema. ConfigReader extracts <key, value> and provides compare_config_key() for fast key matching. AuxRecordReader scans auxiliary indexes with an AuxCompareMode (GREATER_EQUAL / GREATER / LIKE / EQUAL) driving the comparator; tracks the last word seen so a paginated scan can resume. fts_query() walks index and common tables via QueryExecutor::read_by_index() with RecordCallback; fts_write_node() writes auxiliary rows through FTSQueryExecutor::insert_aux_record() / delete_aux_record() with fts_aux_data_t. fts_optimize_write_word() now goes through the same insert/delete path. fts_select_index{,_by_range,_by_hash} return uint8_t (was ulint) with a simpler control flow. fts_optimize_table() binds a thd to its transaction whether invoked from a user thread or the FTS optimize thread. fts_optimize_t drops its fts_index_table and fts_common_table fts_table_t fields; fts_query_t drops fts_common_table. storage/innobase/fts/fts0sql.cc is deleted along with the commented-out and unreferenced parser-era helpers it held. dict_sys.latch is now acquired once per fts_sync_table(), fts_optimize_table() and fts_query() call to open every auxiliary and common table in one pass, instead of being re-acquired per table. Every FTS trx_create() site now takes the THD from a real caller (user trx, fts_opt_thd, DDL ctx->trx, ha_thd()) instead of either leaving it NULL or pulling current_thd. fts_commit(): Each fts_commit_table() used to create transaction and commit, producing N undo segments, N read views and N group-commit batches for N FTS tables touched by the user statement. fts_commit() now creates one internal trx, runs every fts_commit_table() under it, and commits once. fts_optimize_word() could fail when source nodes have equal boundary doc_ids. Loosen the merge predicate from '>' to '>=' so legitimate overlapping ranges that occur when nodes fill to FTS_ILIST_MAX_SIZE at doc_id boundaries are handled correctly. INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE (i_s.cc) wires the shared words_heap via set_words_heap() and empties it per batch with mem_heap_empty(); the per-fetch cleanup frees only the per-node ilist (ut_alloc'd) and skips fts_word_free(). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
cleanup: silence (wrong) maybe-uninitialized warning gcc 15.2.1 with -Og: filamdbf.cpp:355:33: error: 'tfp' may be used uninitialized [-Werror=maybe-uninitialized] 355 | } else if (fread(tfp, HEADLEN, 1, infile) != 1) { | ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MDEV-40540 ST_GeomFromWKB stack overrun on deeply nested GeometryCollection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40366 OOB read on malformed `Format_description_log_event` Neither the binary-parsing Format Description Event constructor nor the constructor-bypassing `get_checksum_alg()` function validated the content length of the passed event buffer. If they receive an FDE with undersized contents, they would obtain corrupt results from an erronous memory location, if not outright crash the program with that memory error. This commit fixes both sites by adding content length checks. Because the goal is not to solve the existence of two binary parsers, `get_checksum_alg()` receives a check duplicated from the Format Description constructor and is no longer a function that never errors. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39788: Remove added line in `master.info` format The line-count lines in `master.info` and `relay-log.info` have been inconsistent (off by one) since their introduction. MDEV-37530 “fixed” this with its common code merger by chance, changing `master.info` to use `relay-log.info`’s line-count definition. This change, howëver, affected backward compatibility, as `master.info` now expects an ignored MySQL-only line where the first `key=value` option, `master_use_gtid`, is. Since this legacy text-based format has limitations that make it due for replacement, only code reüsablility is valuable, and its consistency does not outweigh its compatibility. Therefore, this commit solves this problem without reverting code by: * Changing the writing code to be compatible with both interpretations (albeit inconsistent with the reading code) * Adding a shim entry to `master.info`’s list to emulate prior versions’ reading behaviour * Although this solution can only restore upgrade compatibility with versions 10.0+, versions before MariaDB 10 have long been EOL. While here, this commit also fixes code and comments that contradict the actual effect. [P.S.] The test for this regression is pushed to 10.11 in PR #5147. Reviewed-by: Brandon Nesterenko <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| move DuckDB to a separate package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
KhaledR57
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37865: IF() function is returning incorrect error get_expr_function_type() compared a parsed function name against function_table[] using strncasecmp() bounded by the length of the parsed name. The comparison stopped at that length and never checked that the table entry ended there, so any name that is a prefix of a known function resolved to that function. "if" matched "ifnull" and produced a misleading arity error. Names that prefixed a function with a compatible signature were worse and dispatched silently: l("AB") ran lcase(),t(" x ") ran trim(). A typo in a test script ran a different function and the test still passed. Store each entry's length in function_table[] with STRING_WITH_LEN() and require it to match the parsed length. Report the unmatched name in the "Unknown function" error. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ParadoxV5
paradox.ver5@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40298 Use-After-Free when SQL Thread stops in the middle of SHOW SLAVE STATUS MDEV-36287 acknowledged that SHOW SLAVE STATUS needed to acquire a mutex lock before accessing the SQL Thread’s THD, as otherwise it may access invalid memory if a concurrent STOP SLAVE deletes the THD. But it missed that the SQL Thread’s mutex is `mi->rli.run_lock`, not `mi->run_lock`, so the bug was still not fixed. This commit fills in the oversight by acquiring the correct corresponding lock for each of `Slave_IO_State` & `Slave_SQL_State`. Reviewed-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
github copilot review instructions Assisted-By: Claude:claude-4.8-opus |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Merge branch '11.4' into '11.8' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Arcadiy Ivanov
arcadiy@ivanov.biz |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40523 Versioned UPDATE on a HEAP table with blobs corrupts the history row A system-versioned `UPDATE` of a blob column on a `HEAP` table stored garbage in the history row, and an `AFTER UPDATE` trigger reading `OLD.<blob>` saw the same garbage. Both values are durable: the history row is what `SELECT ... FOR SYSTEM_TIME ALL` returns, and both reach replicas through the row-based binlog image. No ASAN build is needed to reproduce either. ## How a versioned UPDATE reaches the engine The row is first updated in place with `ha_update_row(old_data, new_data)`. The SQL layer then calls `vers_insert_history_row()`, which restores the pre-update row from `record[1]` into `record[0]`, stamps it with the delete-time and calls `ha_write_row()`. So the record handed to `ha_write_row()` is a verbatim copy of the row the engine was just told to overwrite -- blob data pointer included. A versioned `DELETE` is not affected: `TABLE::delete_row()` stamps the end field with `vers_update_end()` and issues a single `ha_update_row()`. It writes no history row. ## Cause `heap_update()` and `heap_delete()` do not free the old blob chain outright. They park it, because the SQL layer keeps reading the pre-update row out of `record[1]` after `ha_update_row()` returns -- `binlog_log_row()` builds the before-image from it, and an `AFTER UPDATE` trigger reads `OLD.<blob>` from it. Those are zero-copy pointers straight into `HP_BLOCK`, so freeing the chain would make them dangle. `heap_write()` redeemed that parking unconditionally, before allocating. For the history row that is exactly the wrong moment, since it sources its blob from the chain the update just parked. The free put those records on the delete list, where the allocation immediately below handed them straight back as the history row's own chain -- with `hp_push_free_block()`'s free-list links already scribbled through the payload. Source and destination of the blob copy overlapped, and `record[1]` was left pointing at reused memory for the rest of the statement. ## What triggers the bug, and what reads the corrupted data Triggering statements are every `vers_insert_history_row()` caller reaching a `HEAP` table whose record buffer was filled by a read: single-table `UPDATE`, multi-table `UPDATE` (both the on-the-fly and the deferred `do_updates()` path), `INSERT ... ON DUPLICATE KEY UPDATE`, and the row-based replication applier. A versioned `UPDATE` that does not change the blob column is unaffected -- `heap_update()` keeps the chain and parks nothing. Three consumers then read corrupted data, all of them out of `record[1]` after the history-row write has recycled the chain: - the history row itself, as returned by `SELECT ... FOR SYSTEM_TIME ALL`; - the row-based binlog before-image, so the corruption reaches replicas; - `AFTER UPDATE` triggers reading `OLD.<blob>`, so whatever the trigger does with that value -- typically writing it to an audit table -- stores wrong data, and that write is itself replicated. The trigger case is the easiest to miss, because `LENGTH(OLD.<blob>)` is still correct: the length lives in the record buffer and survives, and only the payload has been recycled. A `BEFORE UPDATE` trigger on the same table reports the correct value, which localises the damage to the history-row write that happens between the two. ## Fix The parked chain already holds exactly the bytes the history row needs -- it is a verbatim copy of the same record. So instead of freeing it and allocating a duplicate, the new row adopts it: - `hp_flush_unaliased_blob_free()` redeems every parked chain **except** ones the record being written still sources blob data from. - `hp_write_blobs()` takes those over: the stored row points at the parked chain and no new chain is written. The pending slot is cleared only once every column has succeeded, so the rollback path can tell an adopted chain from an allocated one and leaves it parked rather than freeing it. Both record buffers stay valid, because the chain's contents are never disturbed. Adoption also needs no space at all, which matters at `max_heap_table_size`: the history row previously had to find room for a second copy of a blob that was already resident, and the parked chain was often the only reclaimable space. Aliasing is detected by exact pointer equality against the parked chain head. `hp_read_blobs()` hands out zero-copy pointers of exactly two forms -- the chain head, or `chain + recbuffer` -- and reassembles a multi-run chain into `info->blob_buff`, which can never alias, so the two comparisons in `hp_blob_sources_chain()` are complete and need no walk of the `HP_BLOCK` tree. Matching is per blob column, since `pending_blob_chains[i]` is the chain parked for column `i`, and that slot is `NULL` for a column the update did not change. An adopted chain may be longer than the adopting row's blob length: `UPDATE t SET f = LEFT(f,6)` leaves the shortened value pointing at the original data. That is harmless. `hp_read_blobs()` picks the chain layout from the chain's own flag byte rather than from the length, so a short read off a long chain still starts at the right offset, and `hp_free_run_chain()` walks `run_rec_count`/`next_cont` rather than the length, so the whole chain is still reclaimed. `REPLACE` was never affected: `HA_EXTRA_WRITE_CAN_REPLACE` makes `hp_read_blobs()` copy rather than hand out zero-copy pointers. Internal temporary tables never park -- they free their chains outright and do not allocate the array -- so `hp_write_blobs()` guards on it. A blob cannot itself be indexed in a `HEAP` table: `ha_heap` does not set `HA_CAN_INDEX_BLOBS`, so both `KEY (f(10))` and `UNIQUE (f)` are rejected at `CREATE` with `ER_BLOB_USED_AS_KEY`. There is therefore no versioned blob-as-key combination for adoption to get wrong. Blob key segments exist only for internal temporary tables, which never park a chain and are never versioned. ## Tests `storage/heap/hp_test_blob_alias-t.c` drives the sequence at the `heap_write()` API for a single-record chain, a zero-copy run and a multi-run chain, and checks both the stored row and the caller's buffer. It also pins adoption itself, by the stored row's chain pointer and by the growth of `block.last_allocated` across the write: exactly one record slot and no chain. The multi-run case is reassembled into `info->blob_buff` and so cannot alias; the test asserts which layout it got, so the coverage cannot silently degrade. `blob_vers_trigger` covers `OLD.<blob>` in triggers across single-table `UPDATE`, multiple blob columns, `ON DUPLICATE KEY UPDATE` and multi-table `UPDATE`, with `BEFORE UPDATE` alongside `AFTER UPDATE` on one table, and non-versioned `HEAP` and versioned `MyISAM` controls. `blob_versioning`, `blob_vers_odku`, `blob_vers_multi` and `blob_vers_repl` cover the history row itself for every `vers_insert_history_row()` caller and replication of the before-image. `blob_versioning` additionally covers the cases where only one of two blob columns changed, so the history-row write sees a mix of parked and empty slots; a blob shrunk in place with `LEFT()`, so the row and its history carry the same pointer with different lengths; an indexed table, so the key loop runs while `record[1]` still holds a zero-copy pointer and a `UNIQUE` key materializes stored blobs into `key_blob_buff`; and the two `ER_BLOB_USED_AS_KEY` rejections. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
KhaledR57
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37859 mysqltest hex() fails on string arguments func_hex() passed every argument through convert_base_helper(), which parses the argument as a base 10 number. A string argument therefore died with "invalid number 'abc' for base 10" instead of being converted. HEX() is the only one of the base conversion functions that accepts a string. In the server BIN() and OCT() are built as Item_func_conv() with fixed bases and are numeric only, while HEX() has a dedicated Item with a separate string path, because HEX() is the counterpart of UNHEX() and has to serialise bytes. Dispatch on the argument type: numeric arguments keep the existing CONV(N, 10, 16) behaviour, string arguments are converted byte by byte with String::set_hex(), which is the same call the server uses in Item_func_hex::val_str_ascii_from_val_str(). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| move DuckDB to a separate package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-37000 disable optimization for Aria remove_key() on affected MSVC The Aria remove_key() helper can miscompile in x64 MSVC builds when optimization is enabled, leading to 'Index is corrupt' during DELETE. Apply the same #pragma optimize("g", off) workaround already used in MyISAM, and limit it to _MSC_VER >= 1930 && _MSC_VER < 1951. The upper bound for affected MSVC is just the newest MSVC version in VS2026, where the bug is no longer reproducible. Also, MSVC release notes https://learn.microsoft.com/en-us/visualstudio/releases/2026/release-notes list multiple fixes for 19.50 x64 optimizer and codegen bugs. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-24245 REPAIR/OPTIMIZE on ARCHIVE table corrupts blob/text data pack_row() and unpack_row() both used record_buffer. After unpack_row(), blob field pointers in record[0] reference data stored in record_buffer. When pack_row() then writes the new packed row into the same record_buffer, it overwrites the blob data that those pointers still reference. Subsequent Field_blob::pack() calls follow the stale pointers and read corrupted data. This only matters in the optimize/repair loop where the same handler reads then writes each row. Normal INSERT has no prior unpack_row() on the same handler. Fix: in the optimize loop, before writing, copy blob data out of record_buffer (the same approach already used by get_row_version2),so that blob pointers no longer reference record_buffer when pack_row() overwrites it. Free the temporary blob buffer after the loop. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Hemant Dangi
hemant.dangi@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-28233: rsync SST script silently runs unencrypted if stunnel is not installed Issue: When ssl-mode required encryption but the means to perform it was missing, the SST scripts silently fell back to a cleartext transfer: - wsrep_sst_rsync: ran over plain TCP when the 'stunnel' binary was absent. - wsrep_sst_mariabackup: socat used a cleartext socket when ssl-mode was set but no usable cert/key was found (encrypt stayed 0). Solution: Abort the SST instead of falling back to an unencrypted transfer when ssl-mode is not DISABLED but encryption cannot be set up: - wsrep_sst_rsync: derive the implicit ssl-mode from the SSL config even when stunnel is absent, then abort with ENOENT if ssl-mode is active and the stunnel binary is not found. - wsrep_sst_mariabackup: after reading the SSL configuration, abort with EINVAL if ssl-mode is not DISABLED but encrypt resolved to 0 (no usable cert/key). Reviewed-by: Jan Lindström <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Merge branch '10.11' into '11.4' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
github copilot review instructions Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||