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
Rex Johnston
PQ: a pre-aggregating worker hands over its grouping table

A worker that pre-aggregates kept two temporary tables of the same shape: the
grouping table its aggregates accumulated into, and the shipping container the
manager reads. At end of records flush_groups() walked the first and wrote every
row into the second -- a memcpy and a keyed insert per group -- so that the
manager could read rows it already had, in a table it could already have been
given. The tables hold the same rows because a worker produces one row per group
and ships one row per group; there was never anything for the second table to do
but hold a copy.

So there is one table now. setup_worker_preagg() builds the shipping container
itself, from the worker's own item list, and the aggregates accumulate straight
into it; flush_groups() is gone, and so is the per-group copy and the second
table's memory. What the manager reads is the table end_update() wrote.

The batch transport is disabled, which is what makes that possible to say
plainly. It carries record images through a buffer rather than handing a table
over, so everything that goes through it is copied twice, and a worker with a
finished table still has to stream it. pwt_create_transport() now builds the
temporary-table transport unconditionally and the debug flag that selected the
other one is gone. The classes are kept, compiled out, in case the streaming
shape is wanted again.

Two things the copy had been hiding had to be fixed before the table could be
handed over.

A rebuild on disk has to be the cross-thread kind. A container is instantiated
cross-thread, because the manager reads and frees what the worker wrote, and
the transport's own spill path passes that through. But a pre-aggregating
worker's table fills while end_update() is writing it, and end_update() rebuilt
it with cross_thread=false: Aria then ties the new handle's allocations to the
worker's thread, and the manager frees them after that thread is gone. The
decision now travels on TMP_TABLE_PARAM, set where the container is created and
read where the rebuild happens. Without it the new test fails with the
allocation crossing threads, named:

    16416 bytes freed by T@3, allocated by T@71 at maria/ma_bitmap.c:242, ...
    sql/sql_select.cc:23926, sql/sql_select.cc:26598

And the memory that rebuild allocates has to change hands. The transport
already moves the charge from the worker's THD to the manager's for a spill it
performs itself, but a spill inside end_update() never passes through the
transport. pwt_end_send() now brackets put_record() and hands anything the call
left outstanding to the sink: working memory a call allocates and frees nets to
nothing there, and the rebuild is what does not.

main.parallel_query_spill covers the new path: twenty thousand groups against a
64K heap, asserting the query ran in the workers, that a row per group arrived,
that the container really was rebuilt on disk, and that the answer matches the
serial one group for group. main.parallel_query_transport loses the halves that
exercised the batch transport, and with them its dependency on a debug build.

Measured against this tree with the same forced-worker sweep of the main suite
(--parallel-worker-threads=4): the same fifty tests fail before and after, the
same fifty by name.

This commit was prepared with Claude Code (Opus 5), which found that the two
tables held the same rows, and identified both the non-cross-thread rebuild and
the unaccounted memory before either could reach a test.

The we found a bug, we need to close the container's index before handing it
to the manager

A pre-aggregating worker leaves its container's index open: AGGR_OP::put_record()
opens it on its first call and nothing since has closed it. When the copy into a
separate shipping container went away, closing it moved to the worker's tidy-up
at the end of execute_and_handoff() -- which is after sink->flush(), and flush()
is what tells the transport this producer is finished.

The manager may claim the container the moment it is marked done. So a manager
that got there before the worker reached its tidy-up called ha_rnd_init() on a
table whose index was still open, and that asserts:

    mariadbd: sql/handler.h:3770: int handler::ha_rnd_init(bool):
    Assertion `inited==NONE || (inited==RND && scan)' failed.
      handler::ha_rnd_init
      pwt_tmp_table_source::next_row
      pwt_manager::drain_and_send
    Query: SELECT col1, COUNT(col2) FROM t1 GROUP BY col1 ORDER BY NULL

Which thread wins is scheduling, so this showed up as main.parallel_query_aggregate
failing about one run in eight with the whole parallel set running at
--parallel=18, and passing every time on its own.

Closing the index is moved to before the flush, where the hand-off is. The
tidy-up still closes it, for the paths that reach the end without handing
anything over; ha_index_or_rnd_end() on a handler that is already closed does
nothing.

pwt_tmp_table_sink::flush() now asserts what the ordering is for: that nothing
this producer had open on the table is still open when the container becomes the
consumer's to read. That turns a race the manager notices only when it wins into
a condition the producer checks every time -- with the close put back after the
flush, main.parallel_query_aggregate fails on the assertion on every run rather
than on one in eight.

This commit was prepared with Claude Code (Opus 5). The crash was found by
running the parallel test set repeatedly under load rather than once; the
producer-side assertion was added so the next mistake of this shape does not
need luck to be seen.
Khaled Riyad
MDEV-40377 Change Server source code to point to new docs (12.3 part)

Replace the remaining Knowledge Base links with their MariaDB
Documentation equivalents, including the 838 URLs in the help tables.
Only URLs change in fill_help_tables.sql.

fill_help_tables.sql conflicts on merge upward; keep the target
branch's version.
Oleg Smirnov
InnoDB: fix unlatched right-move in the parallel scan partitioner
Yuchen Pei
MDEV-40168 Add a testcase where the first argument of the MVI cast is an more complex expression not just a column->path
drrtuy
feat: MDEV-40672 implement basic support for the pluggable aggregate functions
Oleg Smirnov
Extract parallel logic from ha_innobase to separate files
Kristian Nielsen
Semi-sync: Implement support for using GTID in semi-sync ACKs

Implement the necessary logic in class Repl_semi_sync_master_gtid and
related code, so that the semi-sync master can request the slave to put the
GTID in the reply ACK packet, instead of the filename/offset.

In GTID mode, when a slave connects (which it should do using a GTID start
position), the latest GTID in the starting position (if any), as determined
by the list of transactions pending acks, is used as the point at which to
implicit ACK anything pending.

The semi-sync logic is otherwise unchanged, in GTID mode it just uses the
GTID as the transaction identifier instead of the file/pos of the end of the
event group.

This patch only enables the GTID-based semisync for binlog-in-engine, but
the mode works with old binlog implementation as well, and passes all tests
(if enabled by code change).

Signed-off-by: Kristian Nielsen <[email protected]>
Sergei Golubchik
debian, again
Khaled Riyad
MDEV-40377 Change Server source code to point to new docs (10.11 part)

Replace the remaining Knowledge Base links with their MariaDB
Documentation equivalents, and fix the 14 help table URLs pointing at
/README pages that do not exist. Only URLs change in
fill_help_tables.sql.

fill_help_tables.sql conflicts on merge upward; keep the target
branch's version.
Hemant Dangi
MDEV-40501: Assertion `info->type == READ_CACHE || info->type == WRITE_CACHE' failed in reinit_io_cache upon CHANGE MASTER

Issue:
CHANGE MASTER ... FOR CHANNEL with a channel name within
MAX_CONNECTION_NAME can still overflow the OS file name limit once
escaped into the relay log file name. Relay_log_info::init() then
fails to open the relay log, leaving its index file unopened, but
Master_info_index::remove_master_info() unconditionally calls
reset_logs() on it during CHANGE MASTER's error cleanup, which hits
the assertion in reinit_io_cache().

Solution:
Guard the reset_logs() call in remove_master_info() with is_open(),
so a relay log that was never opened is never passed to it. Also use
MY_SAFE_PATH in open_index_file() so an over-length name fails
deterministically instead of silently falling back to a mangled one.
Dave Gosselin
MDEV-15066:  Filter geometry parts by bounding box before the scan

Implements a pre-filtering step to limit the polygons considered by
ST_Intersects and ST_Disjoint to those whose bounding box overlaps the
other operand.

The probe polygon overlaps the bounding box of seven rows in a table
of 239 country outlines.  Four of those rows are multipolygons of 346,
213, 120 and 21 polygons but not one of those 700 polygons overlaps
with the probe.  Yet, ST_Intersects stored all 700 into the scan.

A polygon whose bounding box does not overlap the other operand cannot
intersect it, yet the cost of the scan grows with the number of
polygons considered.  ST_Intersects and ST_Disjoint now leave such
polygons out.  Multipoint, multilinestring, multipolygon and geometry
collection each count the parts that pass the filter and store only
those.  A geometry of one part is unchanged.

Co-Authored-By: Claude Opus 5 <[email protected]>
Oleg Smirnov
Incorporate a pointer to the coordinator into worker context
Khaled Riyad
MDEV-40377 Change Server source code to point to new docs (11.4 part)

Replace the remaining Knowledge Base links with their MariaDB
Documentation equivalents, including the 838 URLs in the help tables.
Only URLs change in fill_help_tables.sql.

fill_help_tables.sql conflicts on merge upward; keep the target
branch's version. .github/pull_request_template.md is deleted in 12.3;
keep the deletion there.
Yuchen Pei
MDEV-40168 Name the MVI internal column in the table, not in the ALTER

add_mvi_key_part() runs in the parser, where make_internal_field_name()
can only look for a free name among the columns the statement itself
defines. For CREATE TABLE that is every column of the new table, but for
ALTER TABLE it is just the columns being added, so

  ALTER TABLE t ADD KEY ((CAST(j->'$.a' AS CHAR(6) ARRAY)))

asks for DB_MVI_1 on a table that already has a DB_MVI_1, and the two
collide once mysql_prepare_alter_table() merges the old columns in.

Rename in mysql_prepare_alter_table() instead, before the merge, where
the table is known: mvi_name_new_vcols() iterates the columns the ALTER
adds, and for each internal MVI column whose name is taken -- by a
column of the table or by another column of the statement -- picks the
first free DB_MVI_<n> and fixes up the key part that names it. Only a
multi-valued index has an invisible key, so the key part is found for
certain.

This runs on the Alter_info copy that ALTER TABLE works on, not on the
parser's, so re-executing a prepared statement names the column afresh.

multi_valued_index_token_size.test no longer has to drop the index and
add it back to make room for the name; what it does instead is rebuild a
table that still has a multi-valued index over it.

Co-Authored-By: Claude Opus 5 <[email protected]>
Hemant Dangi
MDEV-40501: Assertion `info->type == READ_CACHE || info->type == WRITE_CACHE' failed in reinit_io_cache upon CHANGE MASTER

Issue:
CHANGE MASTER ... FOR CHANNEL with a channel name within
MAX_CONNECTION_NAME can still overflow the OS file name limit once
escaped into the relay log file name. Relay_log_info::init() then
fails to open the relay log, leaving its index file unopened, but
Master_info_index::remove_master_info() unconditionally calls
reset_logs() on it during CHANGE MASTER's error cleanup, which hits
the assertion in reinit_io_cache().

Solution:
Guard the reset_logs() call in remove_master_info() with is_open(),
so a relay log that was never opened is never passed to it. Also use
MY_SAFE_PATH in open_index_file() so an over-length name fails
deterministically instead of silently falling back to a mangled one.
Oleg Smirnov
Refactor for better object names and more accurate comments
Monty
More query cache optimizations

- Added query_cache_type == ALWAYS_OFF to turn of query cache permanently
  for new open tables. Without this option storage engines has to call
  query cache for all update querys and invalidate used tables that has
  a small overhead. When running benchmarks without query cache, this
  is the option to use!
- Updated MyISAM, Aria, MYISAM_MRG and InnoDB to support ALWAYS_OFF.
- Setting query_cache_size in a config file will not automatically enable
  the query cache.
- Added query_cache_available() that functions can check if query cache
  can be used or if it is permanently disabled.
- Moved things around in Query_cache::store_query() to get more work done
  outside of query cache mutex.
- Same for Query_cache::send_result_to_client()

Co-author: Thirunarayanan Balathandayuthapani <[email protected]>
- ÃŒnnoDB changes reviewed and improved
Yuchen Pei
MDEV-40168 fix embedded build
Oleg Smirnov
Rename Parallel_coordinator, some other refactorings and clean-up
Kristian Nielsen
Implement support for semi-sync with --binlog-storage-engine

Call report_binlog_update() when semi-synchronous replication is enabled and
using --binlog-storage-engine.

Only AFTER_COMMIT is available. The AFTER_SYNC in legacy binlog has the
property that changes in a committing transaction does not get visible to
other transactions until the slave has acknowledged, avoiding phantom reads
if the master fails permanently just after. This requires the two-phase
commit between binlog and storage engine so that the binlog is written
before the transaction is engine-committed. However, the whole point of
--binlog-storage-engine is to avoid the expensive two-phase commit.

(The ability to avoid phantom reads could be later implemented as an
AFTER_PREPARE option, which would send binlog to slave and await ack before
it is written/committed into the engine).

Some existing semi-synchronous replication tests are adapted to also run in
the binlog_in_engine suite.

Signed-off-by: Kristian Nielsen <[email protected]>
drrtuy
MDEV-41001: cpack now skips data path that brings symbolic links to fix Windows build.
Monty
Added query_cache_use_rw_lock to allow concurrent query cache lookups

The query cache protected all operations with one exclusive lock
(m_cache_lock_status, guarded by structure_guard_mutex). This
serialized every lookup, even though a lookup mainly reads the
query cache structures.

Added a new global variable, query_cache_use_rw_lock (default OFF).
When set, send_result_to_client() takes a shared (read) lock instead
of an exclusive one, which allows lookups to run concurrently. Having
it as an option makes it possible to benchmark both alternatives with
the same binary and then decide which one to keep.

Implementation:
- try_lock() has a new 'read_lock' argument. The new inline function
  try_read_lock() sets it from query_cache_use_rw_lock.
- Readers are counted in m_readers and leave m_cache_lock_status as
  UNLOCKED. Writers wait until m_readers is 0 and are counted in
  m_waiting_writers. Waiting writers have priority over new readers,
  which ensures that invalidations are not starved by cache hits.
- Readers wait on the new COND_cache_read_lock. wake_up_waiters()
  wakes one waiting writer, or all waiting readers if there is none.
- unlock() is split into unlock() and unlock_internal(). The kind of
  lock to release is deduced from m_cache_lock_status, so none of the
  unlock() callers had to be changed.
- send_result_to_client() updates the query list and the statistics
  under structure_guard_mutex when it only holds a read lock. With a
  write lock this is done as before, without taking the mutex.
- If an engine requests invalidation during a lookup done with a read
  lock, the table key is copied and the invalidation is done after
  the lock is released, as invalidation requires a write lock.
- Added the DEBUG_SYNC point "in_query_cache_hit" used by the new test.

With query_cache_use_rw_lock=0 the code works as before.

Did a simple sysbench run with 64 threads and 200000 simple select queries,
all served from query cache. query_cache_use_rw_lock=1 gave a 2.25x
speedup. The difference to an unmodied MariaDB version for the same
test is 2.3 x faster.

Co-Authored-By: Claude Opus 5 <[email protected]>
sjaakola
MDEV-41012 Galera appliers hang with foreign key of types UUID, INET4, INET6

For direct row writes, the certification key for MYSQL_TYPE_STRING and
MYSQL_TYPE_VARSTRING is built by collating the column value and taking the
collation from Field::charset().

The data types implemented on Field_fbt - UUID, INET6 and INET4
report MYSQL_TYPE_STRING, and their charset() is my_charset_numeric,
which is latin1. Their values are however plain binary and accordingly
innodb maps them to DATA_FIXBINARY. Their keys were therefore run through
latin1_swedish_ci, which folds them. That corrupts the key in two ways:

1. A key mismatch for the same row. The FK constraint's referenced key
that is appended for the parent of a child INSERT is
built from the InnoDB record and is not collated, and it does not match
the primary key appended due to the parent row's direct write.
Certification saw no dependency between a child INSERT and a concurrent
parent UPDATE, and two appliers could apply them in parallel causing
a hang or crash.

2. A key collision between distinct rows. The folding is many to one, so
different values collapse onto one key, Certification compares keys byte
for byte, so unrelated rows were treated as the same row. Concurrent
transactions on them certified as a conflict and one was aborted with
ER_LOCK_DEADLOCK.

Fix is for  wsrep_store_key_val_for_row() to skip the collation for
fields that InnoDB stores as binary, using the same condition as
get_innobase_type_from_mysql_type(). This is a no-op for the types that
worked before.

This change requires to bump the application protocol version to level 5.

The commit has also two mtr tests for regression testing.
Dave Gosselin
MDEV-35845:  Propagate a constant into an IN predicate

SELECT * FROM t1 WHERE v IN ('a','b') AND v = 'b' kept both conjuncts
when v is a string column, while the equivalent form written with OR
was simplified to v = 'b'.

Two mechanisms can perform a rewrite.  Multiple equalities handle it
when check_simple_equality() builds an Item_equal, which it does only
if the field's charset allows constant propagation.  Up through 10.5 the
default character set was latin1 whose collation handler supports
constant propagation.  MDEV-19123 made utf8mb4 the default in 11.6, and
the utf8 collation handlers report that they do not support constant
propagation.

The other mechanism is propagate_cond_constants(), which rewrote
the OR form under every collation.  It descends through
change_cond_ref_to_const(), which returns on any node whose
eq_cmp_result() is COND_OK.  Item_func_in inherits that value, so the
IN predicate was skipped.

Implement an optimization in change_cond_ref_to_const() that replaces
the predicant of an IN predicate with the constant from an equality at
the same AND level.  The predicant is compared against every value of
the list, so the existing per-operand test from MDEV-7152 is applied
once for each of them.

Only a predicant whose arguments were all aggregated to one comparison
data type is replaced.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Jacob Williams
MDEV-38757 Fix EXCHANGE PARTITION with generated columns containing AND/OR conditions

EXCHANGE PARTITION fails with ERROR 1736 (Tables have different definitions)
when tables contain generated columns with AND/OR conditions, even when the
expressions are logically equivalent. This occurs because when expressions are
re-parsed (e.g., via CREATE TABLE ... LIKE), the order of arguments in AND/OR
conditions may change, but the comparison was order-sensitive.

The Item_cond::eq() method was implemented to perform set-based comparison
for commutative AND/OR operations. The set-based comparison algorithm ensures
that two Item_cond expressions are considered equal if they contain the same set
of equivalent arguments, regardless of order.

Added comprehensive test case covering:
- Generated columns with OR conditions
- Generated columns with AND conditions
- Multiple generated columns with different AND/OR combinations
- Nested AND/OR conditions

The fix allows EXCHANGE PARTITION to succeed when expressions are logically
equivalent but have different argument ordering, which is correct behavior
since AND/OR operations are commutative.

MDEV-38757 Limit unordered vcol condition comparison to EXCHANGE PARTITION

Review follow-up to the previous commit, which made Item_cond::eq()
compare AND/OR argument lists as sets for every caller. That changed
equality semantics globally and broke main.derived_cond_pushdown, where
conditions that eq() started reporting as equal were dropped from
attached_condition. Reordering AND/OR operands also changes evaluation
order, which is observable when operands are functions, so the relaxed
comparison must not be the default.

Item::Eq_config gains an unordered_conditions flag, defaulting to false,
next to the existing binary_cmp and omit_table_names flags.
Item_cond::eq() compares its argument lists as sets only when that flag
is set, and otherwise reports two distinct Item_cond objects as unequal,
as it did before this patch series. The flag is threaded through
Virtual_column_info::is_equal() and a new mysql_compare_tables()
parameter, and only Sql_cmd_alter_table_exchange_partition passes it as
true, so the relaxed comparison stays confined to the EXCHANGE PARTITION
metadata check.

The set comparison tests containment in both directions rather than
comparing element counts, so an expression also matches a form that
repeats one of its terms, for example

  col1 > 10 and col2 < 100 or col3 > 50
  col3 > 50 or col1 > 10 and col2 < 100 or col3 > 50

Test 5 of parts.partition_exchange_generated_columns covers that case.
Sergei Golubchik
fix mariadb-plugin-defines.cmake for multi-config
Alexey (Holyfoot) Botchkov
MDEV-40394 XML schema fails on self-referencing type.

Copy XMLSchema_user_type information when the type is used recursively.
Copies that were once created are stored in m_c_free and then reused
later to avoid memory issues.
XMLSchema_item::is_validate_done() replaced with ::end_validation().
XMLSchema_group_def::check_type() now checks for circular groups.
Jacob Williams
MDEV-38757 Fix EXCHANGE PARTITION with generated columns containing AND/OR conditions

EXCHANGE PARTITION fails with ERROR 1736 (Tables have different definitions)
when tables contain generated columns with AND/OR conditions, even when the
expressions are logically equivalent. This occurs because when expressions are
re-parsed (e.g., via CREATE TABLE ... LIKE), the order of arguments in AND/OR
conditions may change, but the comparison was order-sensitive.

The Item_cond::eq() method was implemented to perform set-based comparison
for commutative AND/OR operations. The set-based comparison algorithm ensures
that two Item_cond expressions are considered equal if they contain the same set
of equivalent arguments, regardless of order.

Added comprehensive test case covering:
- Generated columns with OR conditions
- Generated columns with AND conditions
- Multiple generated columns with different AND/OR combinations
- Nested AND/OR conditions

The fix allows EXCHANGE PARTITION to succeed when expressions are logically
equivalent but have different argument ordering, which is correct behavior
since AND/OR operations are commutative.

MDEV-38757 Limit unordered vcol condition comparison to EXCHANGE PARTITION

Review follow-up to the previous commit, which made Item_cond::eq()
compare AND/OR argument lists as sets for every caller. That changed
equality semantics globally and broke main.derived_cond_pushdown, where
conditions that eq() started reporting as equal were dropped from
attached_condition. Reordering AND/OR operands also changes evaluation
order, which is observable when operands are functions, so the relaxed
comparison must not be the default.

Item::Eq_config gains an unordered_conditions flag, defaulting to false,
next to the existing binary_cmp and omit_table_names flags.
Item_cond::eq() compares its argument lists as sets only when that flag
is set, and otherwise reports two distinct Item_cond objects as unequal,
as it did before this patch series. The flag is threaded through
Virtual_column_info::is_equal() and a new mysql_compare_tables()
parameter, and only Sql_cmd_alter_table_exchange_partition passes it as
true, so the relaxed comparison stays confined to the EXCHANGE PARTITION
metadata check.

The set comparison tests containment in both directions rather than
comparing element counts, so an expression also matches a form that
repeats one of its terms, for example

  col1 > 10 and col2 < 100 or col3 > 50
  col3 > 50 or col1 > 10 and col2 < 100 or col3 > 50

Test 5 of parts.partition_exchange_generated_columns covers that case.
Rex Johnston
PQ: the workers pre-aggregate a query that has no GROUP BY

SELECT COUNT(*), SUM(b) FROM t ran serially. The workers could pre-aggregate a
GROUP BY, but aggregation over the whole table was refused, because the shape
the grouped path merges through is not there: with no group the server
materialises nothing, and aggregates in end_send_group() straight off the last
join tab -- no aggregation table, no group key, nothing for a partial to be
merged into.

It turns out neither end needs new machinery, and this commit is mostly the
finding of that.

The manager already merges these. end_send_group() reaches Item_sum::add(), and
add() honours a value left by direct_add() for every aggregate the gate accepts
-- COUNT adds a count instead of incrementing, AVG takes a partial sum together
with the number of rows behind it. So the drain primes the aggregates and hands
the row to the plan's own terminal here exactly as it does through end_update()
for a GROUP BY, and the drain needed no case of its own.

The workers accumulate into the grouped machinery with a single group in it.
The layout gives this shape a group of one constant column, and from there it
is the grouped path: a worker keeps one partial in a keyed container, merges
into it through end_update(), and ships one row. Beyond costing a column per
shipped row -- of which there is one per worker -- that is also what keeps
Item_sum_avg::create_tmp_field() packing the count beside the sum, which it does
only for a container that has a group. An average built any other way would have
arrived without its count.

So what is actually new is the gate. The aggregate tests -- the five that can be
merged, no DISTINCT, one argument, an argument a worker can evaluate -- are the
same for both shapes and move out to pwt_aggregates_can_be_merged(). What is
left is the shape question, and which shape to ask about is the query's own
grouping rather than the plan's: a query that names a GROUP BY stays the grouped
question whatever the optimizer then did with it, so a plan with no group key to
merge through is a refusal of that query and not an invitation to read it as
aggregating over everything, which would answer something else. Only a query
that never grouped reaches the second question.

Refused there: a select-list item reading a column outside an aggregate. Its
value is whichever row the terminal happened to hold, and a worker holds a
different one.

An empty result needed nothing. No rows means no groups means no partials
shipped, and end_send_group() still sends its one row at end of records:
COUNT(*) is 0, SUM is NULL, one row and not one per worker. The new test checks
each of those rather than assuming them.

main.parallel_query_aggregate_whole is that test: serial against parallel for
COUNT, COUNT(col), SUM, MIN, MAX and AVG over integer, decimal and double
columns; the empty case; that exactly one row is sent; and that a DISTINCT
aggregate, STD, GROUP_CONCAT and a bare column outside an aggregate are still
refused and run serially. main.parallel_query_aggregate gains the fall-back
case, where the container turns out to have no group index: the constant group
reaches the same fall-back, ships plain rows, and the manager aggregates them a
row at a time.

Known and accepted: adding doubles is not associative, so SUM and AVG over DOUBLE
or FLOAT can differ from the serial answer, and between runs, in the last
significant bit -- which worker summed which chunk is scheduling. Decimal and
integer aggregates are exact and unaffected. The test rounds well clear of the
last bit rather than leaving floats uncovered.

Under a forced-worker sweep of the main suite (--parallel-worker-threads=4)
three more tests differ than before, all of them EXPLAIN text where a query of
this shape now says ALL_parallel or range_parallel: main.costs,
main.func_group_innodb and main.innodb_full_scan. No value changes in any of
them.

This commit was prepared with Claude Code (Opus 5), which established that
Item_sum::add() already honours a directly added partial -- the fact the whole
change rests on -- and found that a container without a group would have
dropped an average's count.
Raghunandan Bhat
MDEV-40422: use-of-uninitialized-value in my_convert

Problem:
  CAST(expr AS CHAR) has nothing to convert when the argument already
  has the requested character set, so it returns a String that reuses
  the argument's buffer. `Item_copy_string::copy()` compares String
  objects and not their buffers, so it does not see re-usage of buffer
  and copies the buffer onto itself. `String::copy()` needs one byte
  more for the terminating '\0' and adjusting it frees the old buffer
  before allocating the new one, so the copy reads freed memory.

Fix:
  Make sure the source owns the data before copying it into the
  destination.
Kristian Nielsen
Semi-sync: Refactor in preparation for using GTID in semi-sync acks

This is a refactor patch that contains no/little logic changes but a lot of
mostly mechanic code changes to prepare for allowing to use either old-style
filename/offset or new-style GTID to identify an event group in the
semi-sync ack.

The idea is to replace all explicit filename/offset function arguments with
a generic Repl_semi_sync_trx_info *inf to identify an event group (aka
"transaction"). This object can then be used to look up in the semi-sync
hash table by either file/pos or by GTID.

The classes Active_tranx and Repl_semi_sync_master are sub-classed into
Active_tranx_file_pos/Active_tranx_gtid and
Repl_semi_sync_master_file_pos/Repl_semi_sync_master_gtid. Virtual functions
are implemented in each for comparing identifiers and for calculating hash
keys, using either the file/pos or the GTID as appropriate. This way, the
existing logic can now be used with either (only file/pos is actually used
in this patch; adding GTID is for a subsequent patch).

For the binlog writing side, report_binlog_update(), wait_after_sync(), and
THD::semisync_info are extended to also take the GTID of the event group,
which is already available in the calling code.

For the dump thread / slave connection side, update_sync_header() is
extended to take also the GTID. The dump thread code is extended to keep
track of the GTID of the current event group (slightly extending the logic
already there to keep track of event groups). Also, the dump thread now only
passes the last event of an event group into the semisync layer (the other
events are redundant, as they are never semi-sync ack'ed, saving needless
semisync locking and hash lookups).

Signed-off-by: Kristian Nielsen <[email protected]>
Kristian Nielsen
Semi-sync: Some few after-review fixes

Signed-off-by: Kristian Nielsen <[email protected]>
Jan Lindström
MDEV-40903 : query 'reset master' failed: ER_BINLOG_IN_USE (4243) in Galera tests

RESET MASTER fails with ER_BINLOG_IN_USE when a binlog dump thread is
still lingering after a slave disconnects, causing sporadic failures.

Replace direct RESET MASTER in galera, galera_sr and galera_3nodes tests
with include/reset_master.inc, which kills leftover dump threads and
retries the RESET MASTER. Redundant preceding calls to
include/kill_binlog_dump_threads.inc are removed, as reset_master.inc
already does that itself.

Note that all of affected test cases are not vulnerable of failure,
they are here modified for completeness.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Khaled Riyad
MDEV-40377 Change Server source code to point to new docs (11.8 part)

Replace the remaining Knowledge Base links with their MariaDB
Documentation equivalents, including the 838 URLs in the help tables.
Only URLs change in fill_help_tables.sql.

fill_help_tables.sql conflicts on merge upward; keep the target
branch's version.
Jacob Williams
MDEV-38757 Fix EXCHANGE PARTITION with generated columns containing AND/OR conditions

EXCHANGE PARTITION fails with ERROR 1736 (Tables have different definitions)
when tables contain generated columns with AND/OR conditions, even when the
expressions are logically equivalent. This occurs because when expressions are
re-parsed (e.g., via CREATE TABLE ... LIKE), the order of arguments in AND/OR
conditions may change, but the comparison was order-sensitive.

The Item_cond::eq() method was implemented to perform set-based comparison
for commutative AND/OR operations. The set-based comparison algorithm ensures
that two Item_cond expressions are considered equal if they contain the same set
of equivalent arguments, regardless of order.

Added comprehensive test case covering:
- Generated columns with OR conditions
- Generated columns with AND conditions
- Multiple generated columns with different AND/OR combinations
- Nested AND/OR conditions

The fix allows EXCHANGE PARTITION to succeed when expressions are logically
equivalent but have different argument ordering, which is correct behavior
since AND/OR operations are commutative.

MDEV-38757 Limit unordered vcol condition comparison to EXCHANGE PARTITION

Review follow-up to the previous commit, which made Item_cond::eq()
compare AND/OR argument lists as sets for every caller. That changed
equality semantics globally and broke main.derived_cond_pushdown, where
conditions that eq() started reporting as equal were dropped from
attached_condition. Reordering AND/OR operands also changes evaluation
order, which is observable when operands are functions, so the relaxed
comparison must not be the default.

Item::Eq_config gains an unordered_conditions flag, defaulting to false,
next to the existing binary_cmp and omit_table_names flags.
Item_cond::eq() compares its argument lists as sets only when that flag
is set, and otherwise reports two distinct Item_cond objects as unequal,
as it did before this patch series. The flag is threaded through
Virtual_column_info::is_equal() and a new mysql_compare_tables()
parameter, and only Sql_cmd_alter_table_exchange_partition passes it as
true, so the relaxed comparison stays confined to the EXCHANGE PARTITION
metadata check.

The set comparison tests containment in both directions rather than
comparing element counts, so an expression also matches a form that
repeats one of its terms, for example

  col1 > 10 and col2 < 100 or col3 > 50
  col3 > 50 or col1 > 10 and col2 < 100 or col3 > 50

Test 5 of parts.partition_exchange_generated_columns covers that case.
Luke Lu
MDEV-35732 Failed ALTER TABLE leaves cached table metadata corrupted

A failed ALTER TABLE ... RENAME INDEX ... ALGORITHM=INSTANT changed the
outcome of a subsequent, unrelated ALTER TABLE ... ADD FOREIGN KEY. A
failed statement must have no side effects, but here the second statement
wrongly succeeded where on a fresh table it correctly fails with
ER_DUP_KEYNAME.

Root cause: while rebuilding the key list, mysql_prepare_alter_table()
handled a RENAME INDEX request by clearing HA_GENERATED_KEY in place on
key_info->flags. key_info points into the (possibly cached) TABLE object
that is reused across statements. The ALGORITHM=INSTANT incompatibility is
only detected later, after mysql_prepare_alter_table() has returned, so the
statement fails with ER_ALTER_OPERATION_NOT_SUPPORTED with the cleared flag
never restored. The cached generated FK-support index (fk1) was thus left
permanently marked as user-defined.

That corrupted flag flips the de-duplication tie-break in the next ALTER:
adding FOREIGN KEY ind1 (b) creates a generated support index on column b
that prefix-matches fk1(b). Normally fk1 (generated) is dropped and the new
ind1 survives, colliding by name with the existing user index ind1(a) and
raising ER_DUP_KEYNAME. With fk1 no longer marked generated, the new ind1
is dropped instead, so no name collision is reached and the ADD FOREIGN KEY
silently succeeds.

Fix: do not mutate the cached key_info->flags. Track the "renamed => no
longer generated" decision in a per-key local variable (generated_key),
initialised from the flag, set to false on rename, and passed to the Key
constructor. This preserves the in-statement behaviour while leaving the
cached TABLE metadata untouched, so a failed ALTER has no lingering effect.

All new code of the whole pull request, including one or several files that
are either new files or modified ones, are contributed under the BSD-new
license. I am contributing on behalf of my employer Amazon Web Services, Inc.
Sergei Golubchik
fix the build for -G "Ninja Multi-Config"