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
Aleksey Midenkov
Use index in TR_table::query

TR_table::query() used table scan. Now utilize the index if possible,
with minimum validity detection. Getting trx_id by commit_ts is still
suboptimal is it does two index accesses (as limited by fields in
commit_ts index).

When the index detection fails TR_table::query() falls back to
original table scanning method.
Marko Mäkelä
Clean up the tests
Sergei Golubchik
userstat: stack overflow on DROP TABLE with many index statistics

del_global_index_stats_for_table() collected all matching entries
into an array of MAX_INDEXES elements. Statistics of renamed indexes
stay in global_index_stats under their old names, so a table can have
more entries than that and DROP TABLE wrote past the array.

Delete the entries in batches of MAX_INDEXES.

Assisted-by: Claude:claude-opus-5-5
Vladislav Vaintroub
CONC-852 GnuTLS: actually load CRLs from ssl_crlpath directory

GnuTLS has no gnutls_certificate_set_x509_crl_dir(), so
ssl_crlpath was silently ignored, letting revoked server
certificates through. Enumerate the directory and load each
regular file as a CRL instead.

Assisted-by: Claude:claude-sonnet-5
Alessandro Vetere
MDEV-39092 BACKUP SERVER: read the Aria index header under its locks

The header blocks of an Aria index file have no page checksum, and the
table state at their start is written with my_pwrite(), outside the
page cache: by a checkpoint, by maria_close(), and on the first change
after an open. A concurrent copy could read part of such a write, and
the torn state would not be detected.

aria_read_index_file() now reads the header blocks of a table that is
safe for online backup in aria_read_header(), under THR_LOCK_maria,
which maria_open() and maria_close() hold, and under
MARIA_SHARE::intern_lock of the open table, which the other writers
hold. The table is found by the real path of its index file, which
ARIA_BACKUP_CONTEXT::index_name now stores. While the header is read,
no Aria table can be opened or closed; this is one read of the header
blocks per table.

Not covered: _ma_mark_file_crashed() writes the 2-byte "changed" field
without these locks. It runs only on an error path, when the table is
already corrupted. mariabackup calls aria_read_index() directly and is
not changed.

backup.backup_aria_header: while BACKUP SERVER reads the header of an
open table, the open of another Aria table waits for THR_LOCK_maria and
the first write to the open table waits for SHARE::intern_lock. Without
either lock, the test fails.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-39092 BACKUP SERVER: copy Aria tables from the open files

The tables that are not safe for online backup were copied by name,
after aria_open_files_for_backup() had opened and checked their files.
A file could be replaced between the two opens, for example by a
symbolic link that aria_open_files_for_backup() refuses. These tables
are now copied from the open files, both at BACKUP_WAIT_FOR_FLUSH and,
for the deferred tables in the mysql schema, at BACKUP_LOCK_COMMIT.
Outside Windows, backup::copy_or_stream() copies a duplicate of the
open file descriptor, so that the file can still be cloned or copied
in the kernel.

backup.backup_aria_symlink: tables with DATA DIRECTORY and INDEX
DIRECTORY, and a table in a database directory that is a symbolic
link, are backed up and restored. A .MAI or .MAD file that is a
symbolic link into the data directory makes BACKUP SERVER fail.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
Aria: ma_commit() flushes the log up to an uninitialized LSN

If translog_write_record() failed for the COMMIT record, ma_commit()
still called translog_flush(commit_lsn), with commit_lsn not
initialized. The | operator was there so that trnman_commit_trn() is
always called; that call is a separate statement since 91bc9cc9e66.

Call translog_flush() only if the COMMIT record was written, as the
other callers of translog_write_record() do.

Under Valgrind, maria.aria_log_create_error reports the uninitialized
value without this change.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-39092 BACKUP SERVER: fail if the Aria log is read-only

After a log write error, the Aria log is read-only and translog_flush()
returns success without writing. The log files can then end before the
end of the backup log, which maria_backup_end(BACKUP_LOCK_COMMIT)
sampled. The log copy could succeed while recovery of the backup stops
before that end, with data pages from after it. Fail with
ER_ERROR_ON_WRITE if the log is not in the TRANSLOG_OK state.

The test needs a non-debug build, because translog_stop_writing()
fails a debug assertion.

Assisted-by: Claude:claude-opus-5-5
Qingping Zhu
MDEV-PTRC: Document nested-loop ref cache design

Problem/Background:
The PTRC join cache patch needs community-facing context that explains
why MariaDB subquery_cache and JOIN_CACHE do not cover repeated JT_REF
row-batch replay, and which parts of the implementation are deliberate
phase-1 limits.

Solution:
Add design, implementation, validation, performance, commit-series,
and community-submission notes for the nested-loop ref cache. Include a
Jira/MDEV draft, review checklist, known gaps, and a benchmark
reproduction package. The package keeps standard TPC-H Q17 only as a
correlated-subquery switch-matrix sanity check; the performance claim is
based on the TPCH-derived repeated nested-loop join case.

Refresh the performance report with the June 25, 2026 release-build
run. The benchmark harness now applies optimizer_switch setup and the
measured query in the same client connection so session switches are
honored during timing.

Compatibility/Risk:
The performance report presents the timing data as local engineering
evidence, not as an official benchmark result. The docs call out the
rds_ variable prefix, global cumulative status counters, missing BKA
and semi-join tests, and other deferred support.

Test:
Ran bash -n on run_ptrc_benchmark.sh.
Ran git diff --check.
Ran release-build benchmark with the TPCH-derived nested-loop join
case, five measured rounds per switch mode. Q17 was run only as a
sanity check.
Ran release MTR in the previous test commit.
bsrikanth-mariadb
MDEV-39226: Push whole multi-table update/delete down into engines

Give storage engines a way to take over an entire multi-table
UPDATE/DELETE, the way they can already take over a SELECT. Without it the
join, the row matching and every modification run in the SQL layer even
when an engine could do the whole statement itself in one step; a
single-table UPDATE/DELETE already avoids this via
direct_update_rows()/direct_delete_rows(), but a multi-table statement has
no primary handler object to drive that path.

This adds a generic, engine-agnostic pushdown interface: the SQL layer
offers the statement to the engine, and if the engine accepts it, it
performs the whole thing and reports only the row counts.

- Split select_handler into a pushdown_handler base with select_handler
  (result set) and a new multi_upddel_handler (runs a whole UPDATE/DELETE,
  reports row counts, reported as PUSHED UPDATE/PUSHED DELETE); add
  handlerton::create_multi_upddel, looked up in Sql_cmd_dml::execute_inner().
- multi_update/multi_delete gain direct_update_delete_done(), which records
  the engine's counts so send_eof() binlogs and replies without the
  SQL-layer loop; it forces statement-format binlogging so the change still
  replicates under binlog_format=ROW, and errors out instead of silently
  dropping counts for an unsupported result object.
- FederatedX implements the interface as the reference engine used to test
  correctness: it prints the statement back and runs it remotely, passes
  the engine's error code/SQLSTATE through, reads the matched count from the
  remote info string, executes IGNORE locally, and only pushes down when all
  tables share one remote server (same as SELECT/derived/unit pushdown).

Test: federated.federatedx_pushdown_upd_del.
bsrikanth-mariadb
MDEV-41340: Don't push down statements that use a view in FederatedX

A multi-table UPDATE or DELETE referring to a view (merged or
materialized) was pushed down whole to the remote server. The pushed
statement is printed with the view name, which exists only on the local
server, so the remote server failed to find it.

Reject views in get_fed_table_for_pushdown(): a table list entry for
which is_view() is true makes the pushdown decline, and the statement
is executed locally, row by row.

get_fed_table_for_pushdown() is shared by the select, unit, derived and
multi-table UPDATE/DELETE handlers, so SELECT and INSERT ... SELECT
referring to a view are no longer pushed down with the local view name
either.

The view's own body can still be pushed as a derived table (shown as
PUSHED DERIVED in EXPLAIN); only the outer statement is not pushed.

Add tests to federatedx_pushdown_upd_del covering multi-table UPDATE
and DELETE with a materialized (LIMIT) view and with a mergeable view,
a merged view over several tables, a view of a view, a view inside a
subquery, and SELECT and INSERT ... SELECT from views.
Marko Mäkelä
Review revisions

ma_control_file_image(): Validate the fixed part of the header.

maria_backup_control_file(): Minor cleanup.

Clarify some comments.
Alessandro Vetere
MDEV-39092 BACKUP SERVER: recover Aria from the start checkpoint

BACKUP SERVER copied aria_log_control after the log, at
BACKUP_LOCK_COMMIT. The Aria table files are copied earlier, at
BACKUP_WAIT_FOR_FLUSH. A checkpoint between the two copies made
recovery of the backup start from that checkpoint. Recovery then
skipped the REDO for pages that were not dirty at that checkpoint,
but the copied files had older versions of those pages.

maria_backup_start(BACKUP_START): create the backup context, disable
the log purge until maria_backup_end(BACKUP_FINISHED), and sample
last_checkpoint_lsn.

maria_backup_control_file(): write aria_log_control to the backup
with the sampled checkpoint, the last copied log file and
max_trid_in_control_file, instead of copying the file of the server.
ma_control_file_image() builds the file contents.

maria_backup_create(): create a file in the target directory or
start it in the stream; used by maria_backup_copy() and
maria_backup_control_file().

The test backup.backup_aria_checkpoint runs a checkpoint after the
table copy (new DEBUG_SYNC point maria_backup_tables_copied). It fails
with ER_NOT_KEYFILE when aria_log_control has the checkpoint of
BACKUP_LOCK_COMMIT.

Assisted-by: Claude:claude-opus-5-5
Sergei Petrunia
Check test coverage: add DBUG_ASSERT(0) in print_delete_using_table
Aleksey Midenkov
MDEV-39384 Wrong result when selecting from precise-versioned table

SELECT ... FOR SYSTEM_TIME AS OF <timestamp> on a trx-precise table
scans transaction_registry via TR_table::query(MYSQL_TIME&, bool) to
translate the timestamp into a trx_id, comparing each row:

    Item_func_le/ge(Item_field(commit_timestamp), Item_datetime_literal)

transaction_registry is opened directly, bypassing setup_tables(), so
TABLE::map stays 0. Item_field::used_tables() then returns 0, and
Item::const_item() (used_tables() == 0) wrongly reports the field as
const. That makes Arg_comparator::cache_converted_constant() wrap the
field in an Item_cache (its handler timestamp differs from the
aggregated datetime handler), snapshotting record[0] once. The scan then
compares every row against the first row's commit_timestamp -> wrong
result.

Fix: in TR_table::query(MYSQL_TIME&, bool) temporarily set table->map to
a nonzero value for the duration of the scan so used_tables() != 0 and
const_item() is false, disabling the caching. A
DBUG_ASSERT(!field->const_item()) guards the invariant.
bsrikanth-mariadb
MDEV-41243, MDEV-41273 Don't freeze mem_root after engine pushdown

When a statement is pushed down to an engine, the once-per-statement
part of the optimization (e.g. the first_cond_optimization part of
JOIN::optimize(), or the whole of JOIN::optimize() for a pushed down
UNION) is skipped. Re-executing the same prepared statement or stored
routine without pushdown then allocated from a mem_root already marked
ROOT_FLAG_READ_ONLY, hitting an assertion in alloc_root().

Add LEX::dont_freeze_mem_root, set by the pushdown_handler and
derived_handler constructors so it covers every engine implementing
them. Prepared_statement::execute_loop(), sp_head::execute() (through
sp_head::dont_freeze_mem_root) and the SP instruction reparse path no
longer freeze the mem_root of such statements. The flag is only
declared and used in PROTECT_STATEMENT_MEMROOT builds.

The flag is per execution for prepared statements:
Prepared_statement::execute_loop() clears it after each execution, so
the mem_root is frozen again after the first later execution that is
not pushed down. For a stored routine the instruction's LEX keeps the
flag, so once one instruction has been pushed down the routine's
mem_root stays unfrozen, even if later executions are not pushed down,
unless the routine is re-parsed into a new LEX.

Add tests for prepared SELECT, UNION, UPDATE and DELETE, prepared
EXPLAIN of a pushed UNION, derived tables, stored procedures,
functions, triggers, cursors and metadata-invalidation reparse, with
pushdown switched on and off. Most prepared statement and routine
tests check the statements received by the remote server, or EXPLAIN,
to verify that pushdown actually happened.
bsrikanth-mariadb
MDEV-41748: Syntax error with BETWEEN in FederatedX pushdown

A multi-table UPDATE/DELETE pushed down to FederatedX failed on the
remote server with a syntax error when the WHERE clause had a BETWEEN
whose bounds are constants, e.g.

  UPDATE t1 AS a, t1 AS b SET a.x=NULL WHERE a.x BETWEEN NULL AND NULL;

The bounds are wrapped in Item_cache objects. The cache was not filled
yet when the query was printed, so Item_cache::print() emitted
"<cache>(NULL)", which is not valid SQL.

Fix: when printing with QT_PARSABLE, an Item_cache that holds no value
now prints the item it caches. A cache that already holds a value still
prints it as a literal. Output for callers that do not pass
QT_PARSABLE is unchanged. Only FederatedX pushdown uses QT_PARSABLE.

Add a test that pushes down multi-table SELECT, UPDATE and DELETE with
NULL and DATE BETWEEN bounds, and checks both the statements the remote
server received and the resulting data.
Alessandro Vetere
MDEV-39092 test: Aria writes during a streamed BACKUP SERVER

backup.backup_aria_stream_concurrent: BACKUP SERVER WITH 4 CONCURRENT
'cat' streams to 4 archives while one connection inserts into an Aria
table that is safe for online backup, and another inserts into a
TRANSACTIONAL=0 table and into a table without page checksums. The
restored tables pass CHECK TABLE EXTENDED and each holds the rows 1..N
with no gap. The test uses no debug instrumentation, so it also runs
on release builds.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-39092 test: Aria inserts during BACKUP SERVER, no debug needed

backup.backup_aria_concurrent_insert: BACKUP SERVER runs while another
connection commits one row at a time to an Aria table that is safe for
online backup, with a small page cache and small log files. The
restored table passes CHECK TABLE EXTENDED and holds the rows 1..N
with no gap, with N not more than the rows after BACKUP SERVER
returned. The test uses no DEBUG_SYNC or DBUG keywords, so it also
covers release builds, where the Aria log pages have no checksum.
The writer stops when the user lock "run" is released, not on KILL
QUERY: when the kill arrives after a statement of the procedure has
sent its OK, mysql_execute_command() discards it, and the procedure
continues.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-14992 BACKUP SERVER: do not allocate a buffer for an empty file

pread_write() allocated its copy buffer with aligned_malloc(0) when it
copied or streamed an empty file. Valgrind reports a zero-size
allocation ("Unsafe allocation with size of zero"), for example in
backup.backup_aria_stream_concurrent. pread_write() now returns at once
when there is nothing to copy.

Assisted-by: Claude:claude-opus-5-5
Sergei Golubchik
MDEV-41841 OOB read on loose index scan of MIN/MAX with IS [NOT] NULL

QUICK_GROUP_MIN_MAX_SELECT::add_range built the QUICK_RANGE key by
copying min_max_arg_len bytes straight from the SEL_ARG value, which
for an IS [NOT] NULL bound points at the 20-byte is_null_string; a
MIN/MAX column wider than that read past the static buffer.

Build the key image through SEL_ARG::store_min()/store_max() as
every other caller does: they synthesize a full-width NULL key and
never copy from is_null_string, which is sized for memcmp only.

Assisted-by: Claude:claude-opus-4-8
Sergei Golubchik
Item_param::set_param_time and Item_param::set_param_datetime OOB read

Item_param::set_param_time and Item_param::set_param_datetime could
potentially read 4 bytes beyond the value end of the corrupted packet
bsrikanth-mariadb
MDEV-41293: FederatedX: connection state after a net_read_timeout

Problem:
When reading the reply of the remote server times out, the client
library compiled into the server returns without closing the connection
and without setting a client error. FederatedX took the raw
ER_NET_READ_INTERRUPTED for an error of the remote server and went on
using a connection that still had the reply of the failed statement to
arrive. As a result:
* the next statement on that connection failed in various ways, either
  with ER_QUERY_ON_FOREIGN_DATA_SOURCE ("Server has gone away") or with
  an error packet that mysqltest took for a malformed packet;
* inside a transaction, the following statements continued on a new
  remote session in autocommit mode, so they were committed on the
  remote server one by one even though the local transaction was later
  rolled back. The savepoints of the lost session were also still
  remembered by FederatedX.
The same happened when the connection was lost while reading the reply.

Fix:
* After a timeout FederatedX closes the connection, so that the next
  statement opens a new one. The client still gets the error of the
  timeout, ER_NET_READ_INTERRUPTED.
* Whenever the connection is gone after a failed statement, timeout or
  not, the state of the lost session (autocommit mode, savepoints) is
  forgotten and the local transaction is marked to be rolled back, so a
  new connection starts the transaction again. This includes the case
  where the connection is lost while SET AUTOCOMMIT=0 is sent for the
  first statement of the transaction. The closed connection is also
  freed, which was leaked before.
* Errors of the client library are reported as
  ER_QUERY_ON_FOREIGN_DATA_SOURCE by ha_federatedx::info() as well, the
  same way as by the pushed down multi-table UPDATE/DELETE.
* The error state of ha_federatedx, read by info(), is initialized.
* A non-debug build checks the socket for EOF before it sends a command
  (net_clear()). If the remote server has already closed the connection, the
  write fails without the network layer raising an error, and the client
  would get ER_GET_ERRMSG ("Got error 10000 ...") intermittently. When the
  connection is lost and no error has been raised, raise
  ER_QUERY_ON_FOREIGN_DATA_SOURCE with the error of the client library. The
  COMMIT and ROLLBACK of the transaction still return their error to the
  caller.

Test: federatedx_pushdown_upd_del covers the timeout of a pushed down
multi-table UPDATE and DELETE and of reading a result, the statements
after it, the same inside a transaction, a connection killed in the
middle of a statement, a connection lost while SET AUTOCOMMIT=0 is sent
(also followed by a disconnect and a transaction in another session),
client library errors in info(), and that an error of the remote server
which merely has the code of ER_NET_READ_INTERRUPTED is not taken for a
timeout.
The two statements after the idle connection was killed accept
ER_NET_READ_ERROR (reported by the network layer in a debug build) or
ER_QUERY_ON_FOREIGN_DATA_SOURCE (non-debug build).
Aleksey Midenkov
Debug trace for transaction_registry lookups

Add DBUG tracing with the "vers_trx_id" keyword for transaction_registry
lookups done for transaction-precise system-versioned tables:

- TR_table::query(trx_id) and TR_table::query(commit_time, backwards)
  are wrapped in DBUG_ENTER/DBUG_RETURN as "query(trx)" and
  "query(ts)";
- query(ts) prints the searched timestamp ("(b)" marks backwards
  search) and the found transaction_registry row, or "Not found!";
- TR_table::query_sees() prints whether trx_id1 sees trx_id0 and
  why: equal ids, border values, trivial or concurrent visibility
  with the isolation level, or failed query() of trx_id;
- Item_func_trt_trx_sees::val_bool() prints its result, null_value
  and accept_eq.

dbug_print_row() and dbug_print_table_row() get print_names parameter
to print column names along with values. dbug_print_row() now prints
the passed rec instead of always table->record[0].

Example:

  mtrr --mysqld=--debug=d,vers_trx_id,query:i:o,/tmp/good.log bug/v.trx_id,debug
Qingping Zhu
MDEV-PTRC: Cache repeated nested-loop ref rows

Problem/Background:
MariaDB has subquery_cache for subquery expression results and
JOIN_CACHE for batched join execution, but neither memoizes the full
row batch for an ordinary nested-loop JT_REF inner table lookup.
Duplicate outer ref keys can therefore repeat the same handler probe
and scan the same matching inner rows.

Solution:
Add an experimental statement-local partial result cache for safe
JT_REF join tabs. On a miss, the original ref lookup runs and copied
TABLE::record[0] rows are stored for the materialized ref key. On a
hit, cached rows are replayed into TABLE::record[0], and the normal
nested-loop evaluation path continues.

Expose the plan choice through traditional EXPLAIN, JSON EXPLAIN,
optimizer trace, global status counters, and ANALYZE FORMAT=JSON
runtime counters. Add PRC_JOIN and NO_PRC_JOIN table/query-block hints
so reviewers can force or disable the cache for a candidate ref table
without changing the whole session.

Compatibility/Risk:
The feature is disabled by default. Unsupported shapes fall back to
the existing executor path. PRC_JOIN can bypass the optimizer switch
and cost threshold, but it cannot bypass safety checks for non-JT_REF
access, join-buffer plans, outer/semi join inner tables, BLOB rows,
locking reads, triggered ref access, or a zero memory cap.

Correlated subquery result caching remains the responsibility of
MariaDB subquery_cache; this feature does not replace or modify it.

Test:
Covered by later commits in this series.

Known gaps:
No BNL/BKA/BKAH integration, outer/semi join support, BLOB row
caching, LRU eviction, or debug fault-injection tests for handler
error paths.
Alessandro Vetere
Aria: double free after a failed log file creation

translog_create_new_file() adds the new TRANSLOG_FILE to
log_descriptor.open_files before it creates the file. If the creation
failed, the error path freed the object but left the pointer in
open_files. If translog_write_file_header() or
ma_control_file_write_and_force() failed, the error path freed the
object that open_files already owned. In both cases, translog_destroy()
closed and freed the object again at shutdown: "Error on close of
'(null)'", followed by heap corruption.

Remove the pointer from open_files if the file creation fails. Do not
free the object after open_files owns it.

The test needs a non-debug build, because translog_stop_writing()
fails a debug assertion.

Assisted-by: Claude:claude-opus-5-5
Vladislav Vaintroub
CONC-852 GnuTLS: actually load CRLs from ssl_crlpath directory

GnuTLS has no gnutls_certificate_set_x509_crl_dir(), so
ssl_crlpath was silently ignored, letting revoked server
certificates through. Enumerate the directory and load each
regular file as a CRL instead.

Assisted-by: Claude:claude-sonnet-5
Alessandro Vetere
MDEV-39092 BACKUP SERVER: copy offline Aria tables before LOCK_COMMIT

Aria tables that are not safe for online backup (TRANSACTIONAL=0, or
without PAGE_CHECKSUM) were copied at BACKUP_LOCK_COMMIT. A large
table of this kind blocked all commits during its copy.

For a table outside the mysql schema, writes are blocked from
BACKUP_FLUSH on (MDL_BACKUP_DML), and backup_block_ddl() waits for
the running statements and flushes the open tables
(flush_tables(FLUSH_NON_TRANS_TABLES)). So at BACKUP_WAIT_FOR_FLUSH,
maria_backup_table() copies its files as they are.

Tables in the mysql schema that are not safe for online backup are
still copied at BACKUP_LOCK_COMMIT, because
flush_tables(FLUSH_NON_TRANS_TABLES) flushes only TABLE_CATEGORY_USER
tables. Writes to the TABLE_CATEGORY_MYSQL tables (MDL_BACKUP_DML) are
blocked, but their files can be stale. Writes to the system,
statistics and log tables (MDL_BACKUP_SYS_DML) are blocked only from
BACKUP_LOCK_COMMIT on.
mariadb-backup also copies the non-online Aria tables under
BLOCK_DDL and only the statistics and log tables under BLOCK_COMMIT.

backup.backup_aria_offline: at the end of BACKUP_WAIT_FOR_FLUSH, the
files of a TRANSACTIONAL=0 table and of a TRANSACTIONAL=1
PAGE_CHECKSUM=0 table are in the backup and equal to the source, and
an INSERT waits for the backup lock.

Assisted-by: Claude:claude-opus-5-5
Oleksandr Byelkin
Fix false "Thread stack overrun" in local connection queries under ASAN

Problem: send_command() for a mysql_real_connect_local() connection with
its own THD set thd->thread_stack to the address of a local variable.
With ASAN use-after-return detection that variable lives on a fake heap
stack, so check_stack_overrun() computed a bogus stack usage and failed
the query; password_reuse_check then sporadically rejected the first
GRANT on amd64-ubasan-clang-22 builders.

Fix: add THD::set_thread_stack(), which takes the real stack start of the
current OS thread from my_get_stack_bounds(), and use it there and in
THD::store_globals(). Remove the thread_stack= &local hints that were
always overwritten by store_globals() anyway.
password_reuse_check.test now shows the plugin's warnings if the first
GRANT is rejected.

Assisted-by: Claude:claude-opus-5-5
Sergei Golubchik
MDEV-41822 mariadb-binlog ASAN crash on corrupted binlog

1. verify that the null bitmap length doesn't overshoot row event end
2. pass the buffer length to calc_field_size(), so that it'd never read
  beyond the end
bsrikanth-mariadb
MDEV-41749: FederatedX pushdown sends "<nop>(" in pushed-down SQL

A pushed-down statement containing "x < SOME (subquery)" failed on the
remote server with a syntax error, as the query text contained the
internal "<nop>(...)" wrapper. "x < ALL (subquery)" is wrapped in
"<not>(...)" in the same way.

Item_func_not_all::print() prints the wrapper whenever `show` is set.
Omit it when QT_PARSABLE is requested and print the wrapped predicate in
parentheses, as precedence() reports the wrapper, so a parent expression
would not add parentheses itself.

While the ALL/ANY subquery item is intact, args[0] prints the original
"x op ALL|ANY (subquery)". When the subquery is reduced to a constant
("x < ALL (SELECT 2)"), it is replaced by a comparison with the inverted
comparator, and the negation is held by "<not>". Print it explicitly as
"NOT (x >= 2)" for ALL, otherwise the remote server returns the
complementary rows. ANY/SOME needs no negation.

Item_allany_subselect::print() printed its left operand without
parentheses, so "(a XOR b) < ALL (subquery)" was pushed as
"a xor b < all (...)" and parsed by the remote server as
"a XOR (b < ALL ...)". Use print_parenthesised(), as
Item_in_subselect::print() does.

Add tests to federatedx_pushdown_upd_del covering SOME and ALL in
UPDATE, DELETE and SELECT, with results compared with pushdown OFF and
the pushed-down text checked in the remote general log.
Alessandro Vetere
MDEV-39092 BACKUP SERVER: copy Aria data files before the log

BACKUP SERVER copied the Aria log at the start of BACKUP_LOCK_COMMIT
and the .MAD and .MAI files after it. A transactional table can be
written under BACKUP_LOCK_COMMIT, so a copied page could have an LSN
after the end of the copied log, and recovery of the backup failed.
A plain file copy could also read a page while the page cache wrote it.

Aria now copies its table files in maria_backup_step():

BACKUP_WAIT_FOR_FLUSH: DDL is blocked. Copy the tables that are safe
for online backup (TRANSACTIONAL=1 with PAGE_CHECKSUM=1) with
aria_read_index_file() and aria_read_data_file(), which re-read a page
with a wrong checksum. Each file is copied up to its size at the start
of the copy. The log is copied later, after translog_flush() of the
horizon, so it covers every page LSN in the copied files.

BACKUP_LOCK_COMMIT: copy the other Aria tables as whole files, after
the log, as before. Their writes are blocked in this stage.

handlerton::backup_file() and maria_backup_file() are removed. The
generic directory scan copies only the files that is_db_file()
accepts, so it does not copy the .MAD and .MAI files a second time.

backup.backup_aria_rollback: a DELETE on a TRANSACTIONAL=1 Aria table
runs while BACKUP SERVER copies the files. With a small page cache, the
DELETE evicts and writes data pages whose LSN is after the end of the
copied Aria log. Without this change, Aria recovery of the backup fails
an assertion in get_rowpos_in_head_or_tail_page(). Test case by Marko
Mäkelä.

Assisted-by: Claude:claude-opus-5-5
Sergei Golubchik
Wrong table name in the warning about missing statistical table

open_stat_table_for_ddl() printed the global stat_table_name, always
`table_stats`, instead of the name of the table it failed to open.

Assisted-by: Claude:claude-opus-5-5
Sergei Golubchik
userstat: DROP TABLE and DROP INDEX leave stale usage statistics

delete_statistics_for_table() and delete_statistics_for_index()
removed the userstat entries only after opening the statistical
tables, and returned early when that failed.

Delete the userstat entries before opening the statistical tables.

Assisted-by: Claude:claude-opus-5-5
Sergei Golubchik
update C/C
Alessandro Vetere
MDEV-39092 test: torn read of an Aria page in BACKUP SERVER

aria_read_index() and aria_read_data() read a page again when its
checksum does not match, which happens when a concurrent write tears
the read (possible on ext4, for example). No test covered this.

aria_backup_debug_tear(): with the debug keyword aria_backup_torn_read,
change the second half of the first checked page of each read call
once; with aria_backup_torn_read_always, on every read.

backup.backup_aria_torn_read: with aria_backup_torn_read, BACKUP SERVER
succeeds and the restored table is correct; with
aria_backup_torn_read_always, BACKUP SERVER fails with
HA_ERR_WRONG_CRC after MAX_RETRY reads. With MAX_RETRY 0, the first
case fails too.

Assisted-by: Claude:claude-opus-5-5
Sergei Petrunia
Check test coverage(2): add DBUG_ASSERT(0) in print_delete_using_table
Alessandro Vetere
MDEV-39092 test: Aria log rotation before the end of the backup log

backup.backup_aria_log_rotate: after BACKUP SERVER copied the table
files, commits to a table that is safe for online backup continue
until BACKUP_LOCK_COMMIT and make the log continue in at least two new
files. The backup ends in the same log file as the server, and
recovery of the backup applies these commits from the start
checkpoint, over the whole log files, to the copied table files.
The test fails when the log files before the file of the end are not
copied.

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-39092 BACKUP SERVER: end the Aria log after the table copy

The Aria log was copied at the start of BACKUP_LOCK_COMMIT, while
commits were blocked, and before the deferred mysql tables were
copied. Its end was the horizon at that time.

maria_backup_end(BACKUP_LOCK_COMMIT) now determines the end of the log
in the backup (maria_backup_context::end_lsn), after all table files
were copied, and flushes the log up to it. Commits are blocked only
for this. maria_backup_start(BACKUP_FINISHED) copies the log after the
locks were released: the files before the file of the end as a
whole, and the last file up to the page of the end.

The log continues to grow after the end. The pages before the page of
the end do not change. translog_cut_page() changes the page of the end
to the page that translog_flush() wrote when the end was the horizon:
the bytes from the end are filled with TRANSLOG_FILLER, and the page
checksum is computed again. A page with sector protection is
rejected; the server does not write it (TRANSLOG_DEFAULT_FLAGS).
No log rotation is needed.

backup.backup_aria_log_end, based on a test case by Marko Mäkelä:
after the end of the log, an
UPDATE writes log records and data pages while its commit waits for
the backup lock. Before the log is copied, the UPDATE commits and
more writes make the log continue in a new file. The backup contains
none of these changes, ends in the log file of the end, and the
restored server continues the log from the cut page after a kill.
If the end is taken at BACKUP_FINISHED instead, the test fails.

Assisted-by: Claude:claude-opus-5-5
Qingping Zhu
MDEV-PTRC: Test nested-loop ref row replay

Problem/Background:
The executor cache changes need focused MTR coverage because replaying
stored TABLE::record[0] rows changes the read path for eligible ref
join tabs.

Solution:
Add sys_vars coverage for the optimizer switch and PTRC tuning
variables. Add a main test for disabled/enabled execution, VARCHAR and
NULL replay, traditional EXPLAIN, JSON EXPLAIN, ANALYZE FORMAT=JSON
runtime counters, optimizer trace, status counters, cost-threshold
rejection, PRC_JOIN and NO_PRC_JOIN, BLOB and outer-join rejection,
negative entries, memory-cap bypass, and runtime low-hit bypass.

Compatibility/Risk:
The tests keep join_cache_level=0 for supported replay cases so they
exercise the intended phase-1 nested-loop ref path. BKA/BKAH and
semi-join rejection still need stable plan-shape fixtures.

Test:
Ran release MTR:
TMPDIR=/tmp ./mtr sys_vars.partial_result_cache_basic
main.partial_result_cache_join --parallel=1
--vardir=/tmp/mariadb-ptrc-analyze-final-var
--tmpdir=/tmp/mariadb-ptrc-analyze-final-tmp --force

Result: both tests passed; all 2 tests were successful.