Home - Waterfall Grid T-Grid Console Builders Recent Builds Buildslaves Changesources - JSON API - About

Console View


Categories: connectors experimental galera main
Legend:   Passed Failed Warnings Failed Again Running Exception Offline No data

connectors experimental galera main
Sergei Golubchik
UBSAN: sql/table.h:237:16: runtime error: load of value 4, which is not a valid value for type 'bool'

the warning was about uninitialized `bool in_field_list`
let's initialize the whole ORDER when it's allocated.
Aleksey Midenkov
WITHOUT_ABI_CHECK followup

Followup for b337e14440b as info_src takes time too.
info_src does not make much sense without ABI check.
Sergei Golubchik
MDEV-37951 SHOW TABLES allows users with only GRANT OPTION privilege to read all table names in the database "mysql"

WITH GRANT OPTION is not a privilege in the standard sense,
so whenever INFORMATION_SCHEMA visibility dictates "any privilege"
this does not include WITH GRANT OPTION, even though GRANT_ACL
is a privilege in MariaDB.

Assisted-By: Claude:claude-4.6-sonnet
Sergei Golubchik
fix sporadic galera test failures

* query @@datadir before audit is enabled, not directly before reading
  the log. just in case cat gets the log before SELECT is flushed.
* wait for a table to be dropped
Sergei Golubchik
apply reasonable limits to initid.max_length as provided by UDF

UDF can set max_length incorrectly, e.g. to 2138 for an integer.
let's cap UDF-provided value by a type-specific limit.
Daniel Black
MDEV-35738 mariadb build -fsanitize=pointer-compare

invalid pointer pairs are when the length/memory of one string are
intermixed with another.

For comp_err, the end null pointer was compared to soffset within
my_strtoll10. As we didn't need the end position a NULL arg option
was compatble.

For uca-dump, Address Sanitizer raises invalid pointer pair because
argv options (opt) don't have an obvious correlation of having a start
at opt_X.length, even though the lstrncmp makes this true.

The DBUG_ASSERTS of strmov (added MDEV-11752) where incompatible with
pointer-compare. Replaced strmov with static inline version in
m_string.h using memmove that allows overlaps, and being inline allows
the uneeded parts of the implementation to be optimized away.
Sutou Kouhei
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
Daniel Black
MDEV-40488 disable connect.odbc_sqlite3 test on ASAN

Leak appears in libltdl which is opened by the unixodbc
driver. There doesn't appear to any mishandling at the
ODBC level of the connect storage engine.
Sergei Golubchik
MDEV-36147 MariaDB cannot open page-compressed InnoDB tables at startup if innodb_compression_algorithm other than zlib is specified

When innodb_compression_algorithm is set to a non-zlib algorithm (e.g.
lz4) and the provider plugin is loaded from mysql.plugin rather than
command line, InnoDB failed to start because it checked for the provider
at plugin initialization time, before plugin_load() reads mysql.plugin.

Fix:
* InnoDB returns HA_ERR_RETRY_INIT when the compression provider is
  missing.
* In sql_plugin.cc, the retry loop is changed to not reap until
  mysql.plugin has been loaded

Assisted-By: Claude:claude-4.6-sonnet
Aleksey Midenkov
MDEV-39063 Server crashes at Item_func_lastval and Item_func_setval with CTE alias

Pure aliases are not handled properly by Item_func_lastval::val_int()
and Item_func_setval::val_int().

This is followup fix for MDEV-33985 where it missed similar cases for
LASTVAL() and SETVAL().

add_table_to_list() does not create MDL request for pure aliases,
i.e. when there is no table_list->db set or TL_OPTION_ALIAS was
set. When the expression is not inside CTE the case with empty db is
handled by:

  else if (!lex->with_cte_resolution && lex->copy_db_to(&db))
    DBUG_RETURN(0);

So, table_list gets current database name and the query is failed with
ER_NO_SUCH_TABLE error.

The fix adds the case of is_pure_alias() check for val_int() methods
and fails it with ER_NOT_SEQUENCE2 error.

Note: semantics for TL_OPTION_ALIAS cannot be based on empty db, only
parser can set TL_OPTION_ALIAS as resolve_references_to_cte() relies
on TL_OPTION_ALIAS after copy_db_to().
Sergei Golubchik
MDEV-37840 Server crashes when executing FLUSH PRIVILEGES after starting with skip-grant-tables and creating mysql.host table

hash_filo's mutex was lazily initialized in clear(), but grant_reload()
locks acl_cache->lock without first calling clear(). With
--skip-grant-tables, acl_cache is created but clear() is never called
(acl_reload() skips it on error), leaving the mutex uninitialized.

Fix: initialize the mutex eagerly in the hash_filo constructor and
remove the now-redundant init flag.

Assisted-By: Claude:claude-4.6-sonnet
ParadoxV5
Test for MDEV-39788

MDEV-39788 found that the recent refactor on the `main` (now 12.3)
branch missed the (inconsistent) detail that, unlike `relay-log.info`,
`master.info`’s line count _includes_ the line-count line itself.

This commit extends and simplifies the test
`rpl.rpl_read_new_relay_log_info` to `main.rpl_new_info` so it
* Checks this detail to remind future changes of this type of mistake.
* Covers `@@master_info` as well.
* Covers the refactor’s buggy format as a downgrade/revert test.

While here, this commit also includes a new-format version
of MDEV-38020’s test to double as the value read check.

Reviewed-by: Brandon Nesterenko <[email protected]>
forkfun
MDEV-39522 Query with UNION fails in Oracle sql_mode with ER_BAD_FIELD_ERROR/ER_UNKNOWN_TABLE

MDEV-37325 unconditionally wrapped union subqueries in derived tables,
breaking name resolution of outer-scope/correlated references inside
the unions (producing ER_UNKNOWN_TABLE errors).

Delay the wrap until a following operator has a different linkage.
In Oracle mode all set operators share one priority and bind
left-to-right, so wrapping the accumulated prefix on each operator
change enforces it. This also corrects the row multiplicity of mixed
set operations toward left-to-right order.

create_priority_nest(): when the nest covers the whole prefix, point
the wrapper's first_nested at itself. Cut the prefix with
cut_next() before wrapping and re-register it on the outer unit;

Aleksey Midenkov:

In Oracle mode optimize_bag_operation() returns early, so union_distinct is
never recomputed and the stale value reaches execution.
Register the wrapper first, then run fix_distinct(): reset_distinct() now
scans the new outer chain (the wrapper alone) and correctly leaves
union_distinct NULL; a following DISTINCT operand re-establishes it.
Oleksandr Byelkin
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
MDEV-23486 RBR can bypass secure_timestamp=YES

Add tests for system versioning.
INSERT is fixed, but UPDATE is still broken.
Sergei Golubchik
MDEV-40413 ALTER TABLE ... CONVERT ... PARTITION doesn't encode partition names

CONVERT ... PARTITION passed user-specified partition names to
create_partition_name() with translate=FALSE, so names needing
filename escaping (e.g. `foo-bar`) didn't match the on-disk file.
Pass TRUE to encode them.
Sergei Golubchik
MDEV-24598 Duplicate CHECK constraint names are allowed

Field CHECK constraints always have the name of the corresponding field.
Table-level CHECK constraint can be arbitrary named and can have
the name of the field with a CHECK constraint.

Detect this and throw ER_DUP_CONSTRAINT_NAME.

Special treatment for auto-generated constraint names
(CONSTRAINT_1, etc) they can match a field name too - this is not
an error, just auto-generate a different name.

Assisted-By: Claude:claude-4.6-sonnet
Thirunarayanan Balathandayuthapani
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
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
MDEV-23486 RBR can bypass secure_timestamp=YES

fix TIMESTAMP DEFAULT NOW() and ON UPDATE NOW()

Caveat: rbr cannot work without an index - if the slave overwrites
timestamp columns, before-image won't match in full, this is expected.
Daniel Black
MDEV-40414 Server crash in Charset::charset upon JSON operations

JSON_EQUALS didn't check that ::val_str() of its arguments
where not-null before attempting to test their equality.

JSON_OVERLAPS also ensure that ::val_json() isn't null.
Sergei Golubchik
cleanup: get_item_copy<>(item)

make get_item_copy<T>(item) return T* not a generic Item*
helps to avoid casts when a copy needs to be fixed before returning.
KhaledR57
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
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]>
Sergei Golubchik
MDEV-26910 mysqld_multi starts same instance multiple times with the risk to crash database

a group name may be present in a file more than once.
use hash to deduplicate.
Sergei Golubchik
MDEV-30555 The server does not detect changes in NULL-ability of system table columns

Introduce CAN_BE_NULL annotation into Table_check_intact checks.
By default columns are considered NOT NULL, nullable
columns must be explicitly marked with CAN_BE_NULL.

All TABLE_FIELD_TYPE arrays are fixed as above.

Assisted-By: Claude:claude-4.6-sonnet
Sergei Golubchik
MDEV-25813 ASAN errors in err_conv / field_unpack upon multi-UPDATE causing ER_DUP_ENTRY

InnoDB always frees allocated in record[0] blobs even
when reading into record[1].
Let's read into record[0] for consistency.
Daniel Black
Remove no_valgrind_without_big.inc from *C* include dir

The identical file is in mysql-test/include where it is used.
Aleksey Midenkov
MDEV-38854 Assertion table->vers_write fails upon ODKU into table with versioned column

In MDEV-25644 vers_check_update() sets vers_write to false in case it
returns false. It is ok for UPDATE but is not correct for ODKU is bulk
insert requires vers_write on next tuple.

The fix return vers_write value back when vers_check_update() and
related vers_insert_history_row() are done in ODKU.
Sergei Golubchik
MDEV-28743 Roles without grants are handled wrong

In propagate_role_grants_action() (called by grant_reload() on startup
and FLUSH PRIVILEGES), role privilege merging uses a counter-based
bottom-up ordering: each role's counter tracks how many of its granted
sub-roles still need to be processed, and merge_role_privileges() merges
a role only once its counter reaches zero.

The bug: when an intermediate role such as org_role_1 (which inherits
only from a USAGE-only app_role_1) was merged with no privilege changes,
merge_role_privileges() returned 1 as an optimisation to stop upward
traversal.  This prevented the traversal from ever reaching user_role_1
to decrement its counter.  A second leaf traversal (from app_role_2)
could decrement user_role_1's counter only once, leaving it at 1
instead of 0, so user_role_1 was never merged and had no effective
privileges.

The "stop if nothing changed" optimisation is valid for incremental
propagate_role_grants() calls (after a single GRANT/REVOKE), where
every ancestor was already correctly merged.  It is not valid for the
full-reload case, where the counters must be decremented by every leaf
traversal to guarantee correct bottom-up ordering.

Fix: add an initial_load flag to PRIVS_TO_MERGE.  When set (only in
propagate_role_grants_action), the early-stop optimisation is disabled
so traversals always visit all ancestors.

Assisted-By: Claude:claude-4.6-sonnet
Arcadiy Ivanov
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.
Sergei Golubchik
MDEV-40425 handlersocket crashes on read with huge number of fields

* fix handlersocket to unlink it's THD, this apparently was broken
  for years
* add a check for fldnum
* add a first ever handlersocket test
KhaledR57
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
MDEV-31024 Server crash / ASAN use-after-poison in Binary_string::free_buffer / Item_func_sformat::~Item_func_sformat

re-allocate Item_func_sformat::val_arg in shallow_copy()
to keep it in the same memroot as the item.
Vladislav Vaintroub
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
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
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
Merge branch '10.11' into '11.4'
Daniel Black
MDEV-39113 MSAN/ADDR addr2line stack resolver detrimental

MSAN/ASAN test environment, the addr2line was so high in memory
utilization that it was the pick of the OOM killer to resolve the OOM
situation. Once this occurred there wasn't a saved core or gdb backtrace
of the core to resolve the issue.

To resolve this, make stack-trace default to 0 (off) for the addr2line
base implementation under memory sanitizer and address sanitizer.

MariaDB-backup also forces the enabling of stack-trace. Disabling this
unconditionally reduces the risk of a user operational impact if a
lengthy stack trace starting in a mariadb-backup critical locked period.

The mysqld--help test now excludes the stack-trace as its result is
environment dependant. The "Defaults to..." output for suppressed
variables, currently only stack-trace, is excluded.

Since thread-stack is an excluded varable, the ubsan/asan exclusions
from commits dfa6fba9595a and dfa6fba9595a aren't required.
Mohammad Tafzeel Shams
MDEV-40504: Fix memory leak in mariabackup incremental copy

ibx_copy_incremental_over_full() : Replace die() with
proper error handling to avoid memory leaks when RocksDB
backup directory operations fail.