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
Marko Mäkelä
fixup! ea087a30f0b81dbf674d7b1fa2ddb852117818ea
Khaled Riyad
MDEV-41408 MTR prepared statement protocol skipping statements starting with '('

With --ps-protocol, mysqltest runs a SELECT a second time and compares
the results. The regex ps2_re only matched a leading SELECT, so a
statement starting with '(' was executed only once.

Allow any number of '(', each optionally followed by spaces, before
SELECT in ps2_re.
Sergei Golubchik
Merge branch '11.4' into 11.8
Alexander Barkov
MDEV-39563 Implement UPDATE ... RETURNING ... INTO

Adding support for UPDATE .. RETURNING .. INTO queries.

For example:

  UPDATE t1 SET a=10,b=20 RETURNING a,b INTO va,vb;
  UPDATE t1 SET a=10,b=20 RETURNING a,b INTO @a,@b;

Limitations:
1. These types of queries:
  - REPLACE .. RETURNING .. INTO
  - DELETE .. RETURNING .. INTO
  - INSERT .. RETURNING .. INTO
  do not work - they return an error.
  They will be implemented separately, when needed.

2. UPDATE..RETURNING..INTO with --binlog_format=statement is not allowed
  and an error is raised.

3. Using OLD_VALUE(col) inside UPDATE..RETURNING..INTO is not allowed
  and an error is raised.

4. Multi-table updates, as well as single table updates with a subquery
  to the same table in WHERE (which get converted to multi-table) do
  not work and an error is raised.

Notes:

1. ANALYZE and EXPLAIN
  Both
    ANALYZE UPDATE .. RETURNING .. INTO ..
    EXPLAIN UPDATE .. RETURNING .. INTO ..
  return this error:
    'RETURNING..INTO' is not allowed in this context

2. Behavior on no data

  a. In case of degenerated plans (WHERE 1=0, LIMIT 0),
    no errors are raised.

  b. If the updated table contains no rows, the behavior depends on the engine,
    for example:
    - MyISAM returns no errors
    - InnoDB raises
        No data - zero rows fetched, selected, or processed
    This behavior is engine dependent because some engines (e.g. MyISAM)
    quickly know that the table has no records and execute the statement
    using a degenerated plan.

  c. If there are some rows, but non of them match the WHERE condition,
    then this error is raised:
      No data - zero rows fetched, selected, or processed

  d. If some rows where found but none of them actually
    got changed by the SET, still this error is raised:
      No data - zero rows fetched, selected, or processed
    The error message might be misleading. However, if we read
    it as "zero rows [that required updates] fetched", it looks OK.
    Let's not introduce a new error message for now.

3. Behavior on VIEW with CHECK OPTION

  The "too many rows" check is done after the view's CHECK OPTION check.
  With UPDATE IGNORE, a row rejected by WITH CHECK OPTION is skipped
  (with a warning) and does not count as an updated row, so
  UPDATE IGNORE .. RETURNING .. INTO does not raise ER_TOO_MANY_ROWS
  if only one row is actually updated.

Helper changes:

1. The grammar in analyze_stmt_command was changed to have
  LEX::analyze_stmt set to true earlier, so
  LEX::set_returning_into_result() already knows if this
  is an ANALYZE statement.

2. The Sql_cmd_update constructor is now called earlier in the grammar,
  to be able to call Sql_cmd_update::set_with_old_value_items()
  in the SET and RETURNING clauses.

3. Sql_cmd_dml::lex is now set during the constructor time.
  It makes things easier:
  - Sql_cmd_update::returns_result_set() needs the lex.
  - Sql_cmd_delete::orig_multitable and Sql_cmd_update::orig_multitable
    are not needed any more.
    They were used only in Sql_cmd_delete::sql_command_code() and
    Sql_cmd_update::sql_command_code().
    Sql_cmd_dml::sql_command_code() now returns lex->sql_command.
    The overrides Sql_cmd_delete::sql_command_code() and
    Sql_cmd_update::sql_command_code() were removed.

This commit also fixes bugs found during testing:
- MDEV-41262 ER_TOO_MANY_ROWS should be checked after CHECK OPTION in view
  while Update Returning Into
- MDEV-41265 SELECT/ UPDATE RETURNING INTO a single row-field fails with
  ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, even though the column count matches
  This bug was also fixed in 12.3.4.
- MDEV-41260 Misleading message text "uses a LIMIT clause" after Update
  Returning Into
forkfun
MDEV-24684 Quadratic time for MIN/MAX/STD/VARIANCE as window functions

Frame_scan_cursor cleared and re-scanned the whole frame for every row.
Keep the result if the frame did not change (whole-partition frames,
peers), and add only the new rows if just the bottom moved down.
Argument conversion warnings are no longer repeated on each re-scan.
Marko Mäkelä
Merge
Georgi (Joro) Kodinov
more doxygen formatting added.
Dave Gosselin
MDEV-41146:  Check rand() in WHERE when top level base tables are const

A WHERE conjunct that calls rand() but doesn't refer to a column, such
as rand() < 0, is checked on the last non-const top level base table
in the join order.  When every top level base table was const and the
join order ended with materialized semi-joins, the conjunct was never
checked.  Such a query gave wrong results.  In that scenario the join
returns at most one row, so the conjunct is now checked once before
the join starts, together with the conditions on columns of an outer
query.

A join of only const tables already checks it this way, and both cases
now collect it with the same code.  The decision uses the same search
for the last top level base table that places the conjunct in every
other case.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
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.

Note this disables the mem_root protection for the statement for good,
even if later executions are not pushed down, unless the statement is
re-parsed or re-prepared into a new LEX. For a stored routine one pushed
down instruction disables it for the whole routine.

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.
Vladislav Vaintroub
MDEV-39150 Some data conversion macros fail to use memcpy()

MDEV-37788 converted the uintNkorr() and intNstore() macros to use
memcpy() and byte swap intrinsics, but the floating point and short/long
conversion macros in big_endian.h and myisampack.h still accessed data
one byte at a time, and those in little_endian.h were a mix of both.
Compilers may emit slow byte-at-a-time loads and stores for such code.

With memcpy() and the little-endian integer conversion functions, which
are no-ops on little-endian hosts, big_endian.h and little_endian.h no
longer differ. Replace the macros with inline functions in
my_byteorder.h, and remove the two headers, which are no longer
installed. The *get() macros remain, as thin wrappers that pass the
address of their first argument, which also checks its type.

Add float4store_be() etc. for big-endian floating point numbers, and
define mi_float4store() etc. in myisampack.h as aliases of these.

Remove the unused ulongget() macro, and the code for the mixed-endian
floating point layout (a little-endian CPU with big-endian floating
point word order) from the macros, change_double_for_sort() and dtoa.c.
It only applied to the obsolete ARM FPA format.

Reimplement mach_double_read(), mach_double_write(), mach_float_read()
and mach_float_write() in InnoDB with float8get(), float8store(),
float4get() and float4store(), instead of copying bytes in a loop.

The stored formats do not change. The unit test byte_order-t now checks
the byte layout of the floating point macros and the sign extension of
the native byte order macros, so no MTR test is added.
Khaled Riyad
MDEV-38861 heap-use-after-free in Prepared_statement::execute()

DROP PROCEDURE and CREATE OR REPLACE PROCEDURE executed from inside the
routine itself removed it from the SP cache. sp_head::destroy() then freed
the memory root that the running sp_head, its LEX and its instructions
live in, and the caller kept using them.

Skip the removal while the routine is being executed. sp_cache_invalidate()
above has already bumped the cache version, so the stale entry is removed by
the next lookup, after IS_INVOKED has been cleared.
bsrikanth-mariadb
MDEV-40598: Capture sequences used only in column DEFAULT expressions

Problem:
A sequence referenced only in a column's DEFAULT expression (e.g.
"a INT DEFAULT NEXTVAL(s1)") is opened only when a statement
evaluates DEFAULT values (INSERT, LOAD DATA, etc). A plain SELECT
never opens it, so the sequence never appears in
thd->lex->query_tables, and Optimizer_context_recorder::
dump_sql_script() had no way to see it. The dependent table's
definition was then captured without the sequence it depends on,
making the captured context unusable on replay.

Fix:
TABLE::internal_tables lists these sequences. dump_sql_script() walks
it for each dumped table and calls the new dump_sequence_context() for
each entry, before the table's CREATE TABLE so that replay can
recreate both in order. The sequence is not opened; only its database
and name are used, so for now it is captured as a placeholder
"CREATE SEQUENCE IF NOT EXISTS db.name" without its parameters or
current value. The name is qualified with the sequence's own database,
so a sequence in a different database than the table is recreated in
that database.

Such a placeholder is also remembered in a separate hash. If the query
uses the same sequence directly as well, the main loop of
dump_sql_script() reaches it later, finds the placeholder, emits
"DROP SEQUENCE IF EXISTS db.name", and then dumps the sequence's real
definition and current value (SETVAL) as for any sequence used in the
query, regardless of the order the tables are listed in.

To share code with the existing dump loop, the SETVAL logic of the loop
is factored out into dump_sequence_current_value().

Even for the scenarios where sequence is dropped after table creation,
a name is replaced by a view, or a user doesn't privilege on the sequence;
The name is still captured as a sequence.

Tests (opt_context_store_ddls.test): INSERT-opened sequence; plain
SELECT; LOCK TABLES; a user without privilege on the sequence; two
sequences in one table; dropped sequence; stored function in the
query; name replaced by a view; re-execution as a prepared statement;
sequence in a different database; sequence used both directly and as a
DEFAULT dependency, in both orders.
Dave Gosselin
MDEV-41211 Federated multi-table DELETE keeps a const table row

A multi-table DELETE on a FEDERATED or FederatedX table went wrong
when a primary key lookup made the target a const table.  The
optimizer reads a const table's row through index_read_idx_map(),
whose default implementation ends the index scan and frees the result
set.  The server asks for the row's position later, during execution,
so the saved position was empty.  FederatedX skipped the row and
FEDERATED crashed in rnd_pos().

Both engines now override index_read_idx_map() so that the lookup
leaves its result set open, as index_read() does.  position() then
records a valid position, and the result set is freed at the end of
the statement.  FEDERATED's reset() now also clears stored_result,
which otherwise pointed at a freed result set and was freed again
when the table was closed.

Tests include a multitable UPDATE with a const target, which crashed
both engines before the fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sergei Golubchik
Merge branch '11.8' into 12.3
Vladislav Vaintroub
Add WIX_ACCEPT_EULA option for WiX 7 and later

Off by default. Passes -acceptEula wix<major> to wix.exe, only for WiX 7+,
older versions do not know the switch.
Vladislav Vaintroub
CI experiment: really pin WiX to 5.0.2, the last MS-RL release

The previous commit was meant to do this but is empty.
drrtuy
MDEV-40957 MTR  tests to cover DML statements in a replicated setup.
Vladislav Vaintroub
CI experiment: pin WiX to 5.0.2, the last MS-RL release
Pekka Lampio
MDEV-38999: Galera - stale reads in explicit transactions

wsrep_sync_wait is a per-statement-type bitmask (READ, UPDATE/DELETE,
INSERT/REPLACE, SHOW) that forces a causal wait - block until this
node has applied every write already certified cluster-wide - before
executing a matching statement. wsrep_must_sync_wait() unconditionally
skipped this wait whenever the session was already inside a
multi-statement transaction, regardless of which bit was configured
for the statement at hand. Once BEGIN (or, via autocommit=0, the
first statement) had opened a transaction, every later statement in
it was exempt from its own causal wait - so writes could run against
a view no fresher than whatever BEGIN itself happened to check, which
is only the READ bit, regardless of the actual configured mask.

Fix: only skip the wait once this transaction has generated its first
write (wsrep_has_changes(thd)). Every statement up to and including
that first write is still safe to wait for - nothing of this
transaction's is locked yet when the pre-statement check runs - which
closes the gap above. Nothing after the first write re-waits, matching
the original behaviour for the rest of the transaction: once this
session may be holding row locks, a fresh causal wait risks a
self-deadlock against its own local applier (which can be blocked
needing one of those same locks to apply a write-set this wait is
waiting for, with nothing to resolve it but the wait's own timeout).

A second, independent defect surfaced once in-transaction waits could
time out at all: wsrep::client_state::after_command_after_result()
only clears a pending wsrep error once the transaction is no longer
active - correct for a genuinely doomed BF-aborted transaction, but a
causal-wait timeout is a pre-check failure that touches nothing, so
this left every later statement in the same transaction, ROLLBACK
included, failing with the same stale error. wsrep_sync_wait() now
resets the error immediately after reporting it for the one statement
that actually hit it, scoped to this failure path only - genuine
BF-abort/deadlock errors elsewhere keep their correct sticky-until-
rollback behaviour.

Tests: galera_MDEV-38999.test covers the original bitmask gap across
both explicit BEGIN and autocommit=0 transactions, and a repeated-read
scenario proving a satisfied bit isn't cached across statements.
galera_MDEV-38999_locks.test and galera_MDEV-38999_autocommit.test
exercise the self-deadlock risk directly: a transaction already
holding a row lock the paused applier needs must still return promptly
once the applier is released, not block for the full
repl.causal_read_timeout.

Assisted-by Claude AI
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.

Note this disables the mem_root protection for the statement for good,
even if later executions are not pushed down, unless the statement is
re-parsed or re-prepared into a new LEX. For a stored routine one pushed
down instruction disables it for the whole routine.

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.
Dave Gosselin
MDEV-41146:  Check rand() in WHERE when top level base tables are const

A WHERE conjunct that calls rand() but doesn't refer to a column, such
as rand() < 0, is checked on the last non-const top level base table
in the join order.  When every top level base table was const and the
join order ended with materialized semi-joins, the conjunct was never
checked.  Such a query gave wrong results.  In that scenario the join
returns at most one row, so the conjunct is now checked once before
the join starts, together with the conditions on columns of an outer
query.

A join of only const tables already checks it this way, and both cases
now collect it with the same code.  The decision uses the same search
for the last top level base table that places the conjunct in every
other case.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Jan Lindström
MDEV-40038 tp_foreach() crashes on a dying transaction participant

Bug: tp_foreach() found an engine in hton2plugin[] and passed the
result of plugin_lock() to plugin_hton() without a check. While
reap_plugins() deinitializes an uninstalled engine (PLUGIN_IS_DYING),
the slot stays set until the end of ha_finalize_handlerton(), and
plugin_lock() returns NULL. The server crashed in plugin_hton(), for
example in RESET MASTER through ha_commit_checkpoint_request().

This is a regression from aed5928207a, which changed plugin_foreach()
to tp_foreach() and lost the PLUGIN_IS_READY state mask.

Fix: add plugin_lock_ready(), which locks only a PLUGIN_IS_READY plugin
and reports under LOCK_plugin whether a failed lock was for a READY
plugin (out of memory in debug builds). tp_foreach() skips a plugin that
is not READY and returns an error for a READY plugin that cannot be
locked. An uninstalled but busy engine (PLUGIN_IS_DELETED) is not
visited, as with plugin_foreach() before.

The test uses a DEBUG_SYNC point in ha_finalize_handlerton() to run
RESET MASTER while an engine is deinitialized.

Assisted-by: https://mariadb.org/governance/governance-ai-policy/
Georgi (Joro) Kodinov
more doxygen formatting added.
drrtuy
MDEV-40957 replication from InnoDB into DuckDB works using FULL mode only.
Oleksandr Byelkin
MDEV-41099 HANDLER READ missing column privilege check

HANDLER OPEN accepted column-level SELECT grants as table access, but
HANDLER READ returns all columns and never checked column privileges.
Reopen of a handler table (after FLUSH/ALTER) did no checks at all.

Require SELECT on every column (as for SELECT *) when the user has no
table-level SELECT, and redo the privilege checks on reopen.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
drrtuy
MDEV-40957 DuckDB engine now returns maximum cost for unimplemented index handler operations effectively disabling them.
Marko Mäkelä
fixup! 1479bb3f7422efce56b225e607bc7cfdf028141e

InnoDB_backup::log_track(): Copy entire blocks, and zero out any garbage
at the end.
Pekka Lampio
MDEV-40018: SHOW SLAVE STATUS allocates ~1.7 MB per call due to oversized VARCHAR columns

SHOW SLAVE STATUS / SHOW ALL SLAVES STATUS / INFORMATION_SCHEMA.SLAVE_STATUS
are all served from the ST_FIELD_INFO array slave_status_info[]. Thirteen of
its columns (Replicate_Do_DB, Replicate_Ignore_DB, Replicate_Do_Table,
Replicate_Ignore_Table, Replicate_Wild_Do_Table, Replicate_Wild_Ignore_Table,
Replicate_Ignore_Server_Ids, Gtid_IO_Pos, Replicate_Do_Domain_Ids,
Replicate_Ignore_Domain_Ids, Slave_SQL_Running_State, Replicate_Rewrite_DB,
and Gtid_Slave_Pos) used the no-argument Varchar() helper, which defaults to
MAX_FIELD_VARCHARLENGTH/3 characters. With utf8mb3, that reserves 65532
bytes per column in the temporary table's record buffer regardless of the
actual value size, and the record is allocated twice, accounting for the
reported ~1.7 MB allocated on every call.

Change these columns to Blob(MAX_FIELD_VARCHARLENGTH), which keeps the same
maximum capacity but stores only a pointer and length in the record. Values,
NULL-ability, and column order are unchanged; the client-visible column type
for these columns changes from VARCHAR to BLOB.

mysql-test/suite/funcs_1/r/is_columns_is.result is updated to match, since
it asserts on INFORMATION_SCHEMA.COLUMNS metadata for these columns.

Assited-By: Claude AI
Vladislav Vaintroub
WIX_ACCEPT_EULA: pass the switch only if WiX asks for it

Do not rely on the WiX version. Accept the EULA only if the option is on
and WiX fails with WIX7015, so that a tool without the check, e.g one
built from source, never gets the switch.
drrtuy
MDEV-40957 read-free DML on the slave for DuckDB.
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.

Merging upward: fill_help_tables.sql conflicts at 10.11->11.4 and
11.8->12.3. At those two merges keep the target branch's version,
since 11.4, 11.8 and 12.3 each carry their own URL fix. From 12.3 to
13.1 and 13.1 to main it merges cleanly; take the incoming change.
.github/pull_request_template.md is deleted in 12.3; keep the deletion.
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.

Note this disables the mem_root protection for the statement for good,
even if later executions are not pushed down, unless the statement is
re-parsed or re-prepared into a new LEX. For a stored routine one pushed
down instruction disables it for the whole routine.

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-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.
ParadoxV5
MDEV-38849 slave_connections_needed_for_purge prevents independent machine from purging binary logs

`@@slave_connections_needed_for_purge`’s default of `1` ensures binary
log availability on replication masters, but is not a sensible default
suitable for all scenarios, especially for long-term slave servers and
standalone (not in a replication setup) servers.
The outcome was that standalone server users were confused why automatic
binlog purging does not work.

This commit changes this default to `0`, which is suitable for both
standalone and (when backed by prompt failure recovery)
replication setups.
`0` also more closely matches the behaviour before MDEV-31404,
which added this variable, out of the box.

This commit also adds a one-time replication warning when registering a
slave, but `@@slave_connections_needed_for_purge` is left unchanged.
Rather than enforcing a defence with an unsensible default, this
reminder will bring awareness of the risk of automatic binlog purging.

This commit also cleans up Galera and MTR workarounds to the
introduction of the `@@slave_connections_needed_for_purge=1` default.
drrtuy
MDEV-40957 mixed-mode replication UPDATE/DELETE for DuckDB.
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.

Merging upward: fill_help_tables.sql conflicts at 10.11->11.4 and
11.8->12.3. At those two merges keep the target branch's version,
since 11.4, 11.8 and 12.3 each carry their own URL fix. From 12.3 to
13.1 and 13.1 to main it merges cleanly; take the incoming change.
.github/pull_request_template.md is deleted in 12.3; keep the deletion.
Mohammad Tafzeel Shams
MDEV-35154 : dict_sys_t::load_table() is holding exclusive dict_sys.latch
for unnecessarily long time

Issue:

dict_load_table_one() was invoked with the exclusive dict_sys.latch held
and never released it. The whole load ran inside that one critical
section: reading the SYS_TABLES record, opening the .ibd file, and
reading SYS_COLUMNS, SYS_VIRTUAL, SYS_INDEXES, SYS_FIELDS, the clustered
index root page, and SYS_FOREIGN, as well as loading every table related
by FOREIGN KEY constraints.

Almost all of that is I/O. Because dict_sys.latch is the single latch
that guards every dictionary lookup, one cold table open blocked every
other session from opening any table, including tables that were already
cached. A slow enough load could trigger the fatal semaphore wait check.

Fix:

Split the load into three phases. A short latched phase creates the
table object and publishes it as an incomplete "stub"; the I/O runs with
no latch held; a final latched phase links the FOREIGN KEY constraints.

The stub's progress is held in the top two bits of dict_table_t::
n_ref_count (LOADING_DEF, LOADING_FK, LOAD_FAILED), analogous to how
buf_page_t::state() combines a small lifecycle state with the buffer-fix
count in one atomic. A thread that finds a loading table waits by
pinning it (dict_table_t::try_pin_for_wait(), so it cannot be freed while
unlatched) and acquiring dict_table_t::lock_latch in shared mode, the
same latch the loader holds in exclusive mode for the duration of the
load. dict_sys_t::wait_for_load() reports whether the load it waited for
actually succeeded, so a caller that only needed to wait for the table it
already found can use it directly afterwards instead of repeating the
lookup; a caller that only wanted to confirm a related table had finished
linking its constraints can likewise skip straight to treating it as
already linked. A failed load can be torn down safely even though other
threads may have already taken such a pin to wait on it: try_pin_for_wait()
refuses once LOAD_FAILED is set, and the teardown path drains any pins
taken earlier before freeing the stub.

A table remains hidden (LOADING_FK) until every table related to it by
FOREIGN KEY constraints has been loaded as well, and dict_sys_t::
load_table() makes them all visible in one step. The intermediate state
is visible to constraint linking via dict_sys_t::find_table_fk(), so that
two threads loading tables that reference each other can still link the
constraint between them.

Tables that InnoDB internal SQL can refer to are loaded without ever
releasing the latch. The internal SQL parser is not reentrant and is
serialized only by the exclusive dict_sys.latch, and it opens tables in
the middle of parsing; releasing the latch there let another thread run
the parser concurrently and corrupt its state.

Changes:

-  dict_table_t : hold the progress of loading in the top bits of
  n_ref_count (std::atomic<uint32_t>): LOADING_DEF, LOADING_FK,
  LOAD_FAILED, loading(). Add the loader-only helpers start_loading(),
  advance_to_loading_fk(), load_finish(), mark_load_failed(), and
  try_pin_for_wait() for waiters, all built on the existing lock_latch.

-  dict_load_table_one() : publish the table as a LOADING_DEF stub in
  table_non_LRU and release the latch before loading the tablespace,
  the columns and the indexes; reacquire it, move the table to
  table_LRU and advance it to LOADING_FK before loading the foreign
  key constraints.

-  dict_sys_t::load_table() : wait for a concurrent load of the same
  table via wait_for_load(); allocate the foreign key table names on
  a local heap, because the latch is released while draining them;
  in the drain loop, wait for a table whose definition is still being
  loaded by another thread and skip one that is only waiting for its
  own related tables; make all tables loaded by this invocation
  visible in one step via load_finish().

-  dict_sys_t::wait_for_load() : pins the observed table via
  try_pin_for_wait() (returning false immediately if the load already
  failed), releases dict_sys.latch, blocks on the table's own
  lock_latch, reacquires dict_sys.latch, and only then checks
  loading() and releases its pin, returning whether the load
  succeeded. Because the latch is reacquired before the pin is
  released, a successful wait leaves the table valid to use directly,
  with no need to repeat the lookup that found it. Takes an excl
  parameter: whether the caller holds dict_sys.latch in exclusive
  mode (lock()/unlock()) or shared mode (freeze()/unfreeze()), so it
  knows which pair to use to release and reacquire it.

-  dict_load_table_one_discard() : used wherever a stub must be torn
  down (retry after DB_SUCCESS_LOCKED_REC, column or virtual-column
  load failure, a corrupted index or missing FK index). Calls
  mark_load_failed(), which marks the stub LOAD_FAILED and releases
  lock_latch to wake any already-pinned waiters, then drains the
  reference count to zero before removing the stub.

-  dict_sys_t::add() : take lock_latch in exclusive mode on the
  loader's behalf before a loading stub becomes reachable via
  find_table_any().

-  dict_get_and_save_data_dir_path() : skip re-acquiring lock_latch
  when the table is loading(), because the loading thread already
  holds it in exclusive mode and is the only thread that can reach
  the table at that point.

-  dict_load_hold_latch() : whether a table may be referenced by
  InnoDB internal SQL, and therefore must be loaded without releasing
  the latch: InnoDB system tables, FULLTEXT INDEX auxiliary tables and
  the persistent statistics tables. The FULLTEXT prefix match is
  case-sensitive, matching how these auxiliary table names are
  actually generated.

-  dict_load_table_on_id() : copy the table name and release the
  SYS_TABLES page latch before calling load_table(), which may now
  block. Restore the cursor position only if the scan has to continue.

-  dict_load_foreign(), dict_load_foreigns() : add the fk_heap
  parameter and allocate the names appended to fk_tables on it.

-  dict_sys_t::find_table_any() : an unconditional lookup by table
  name or table_id_t, returning a table even while it is still being
  loaded. Only the name (or id) and hash pointers may be read on such
  a table.

-  dict_sys_t::find_table() : wait for a concurrent load of the table
  it finds, via wait_for_load(), both in the by-name and the by-id
  variant, retrying the lookup if the load failed, since the stub may
  have been freed, or a fresh load of the same name or id may now be
  in progress. Takes an excl parameter so that the caller can state
  which dict_sys.latch mode it holds: dict_acquire_mdl_shared(),
  dict_table_open_on_id(), dict_table_open_on_name(),
  row_purge_remove_clust_if_poss_low(), trx_t::evict_table(),
  UndorecApplier::apply_undo_rec(), trx_purge_table_open(),
  prepare_inplace_alter_table_dict(), lock_release(), and
  create_table_info_t::create_foreign_keys() all pass it.

-  dict_sys_t::find_table_fk() : like find_table(), but LOADING_FK
  tables are returned, so that constraints can be linked into them
  while holding the exclusive latch.

-  dict_table_can_be_evicted() : a table that is being loaded may only
  be freed by the thread that is loading it.

-  dict_foreign_add_to_cache() : resolve both sides with
  find_table_fk().

-  create_table_info_t::create_foreign_keys() : take a temporary
  reference on a referenced table as soon as it is resolved, released
  on every exit path by a scope guard; call dict_sys.prevent_eviction()
  only once the constraint is actually committed to the dictionary
  cache, replacing the temporary reference.

-  create_table_info_t::create_table() : acquire a reference to the
  created table around the foreign key handling, and use a local heap
  for the names of the foreign key related tables.

-  assertion fix : the load path may now run without dict_sys.latch,
  but only in the thread that is loading the table, and a cached
  table is no longer necessarily fully loaded. dict_sys.locked() is
  relaxed to "dict_sys.locked() || table->is_loader()" in
  dict_load_columns(), dict_load_virtual_col(), dict_load_fields(),
  dict_load_indexes(), dict_index_add_to_cache(),
  dict_index_find_cols(), dict_index_build_internal_clust(),
  dict_index_build_internal_non_clust() and
  dict_index_build_internal_fts(). Checks of dict_table_t::cached are
  relaxed and reordered after the atomic loading in
  dict_table_add_system_columns(), dict_sys_t::add(),
  dict_table_rename_in_cache() and hash_insert().

-  innodb.dict_load_concurrent
  A load is parked at dict_load_table_one_no_latch while
  holding no latch; another table can be loaded meanwhile, and a
  second opener of the same table waits. A second case checks that a
  table is not made visible while a table related to it by a FOREIGN
  KEY constraint is still being loaded by another thread.
drrtuy
MDEV-40957 various fixes for mixed DML path that suffered from uninit bitmaps and wrong SQL statements that failed in DuckDB.