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
Monty
Added new mysys functions my_open_dir and improved my_copy

- Improved my_copy() using copy_file_range and memmap

- New mysys functions:
int my_copy_file(File from, File to, myf MyFlags);
int my_copy_file_range(File from, File to, my_off_t start,
                        my_off_t end, myf MyFlags);

- New functions for looping over files in a directory:
MY_NO_CACHE_DIR *my_dir_open();
int my_dir_read_next()
int my_dir_rewind();
int my_dir_close();

Other things
- Fixed #ifdef's in sql_backup.cc to use the new define
  HAVE_COPY_FILE_RANGE
Sergei Golubchik
MDEV-40406 hide #mysql50# under old mode
Abdelrahman Hedia
MDEV-29803: Change mariadb-binlog --gtid-strict-mode default to OFF

The --gtid-strict-mode option in mariadb-binlog was introduced in MDEV-4989
with a default of ON. This causes mariadb-binlog to refuse to display
events when it encounters out-of-order GTIDs, which commonly happens
when replaying a remote binlog into a server and then reading back the
resulting local binlog files.

This is overly restrictive for a diagnostic/display tool. While the
server's gtid_strict_mode makes sense as a safety mechanism, applying
the same strict validation by default in the client tool prevents users
from even inspecting problematic binlog files.

Change the default to OFF so that mariadb-binlog processes binlog files
without erroring on out-of-order GTIDs by default. Users who want strict
validation can still explicitly pass --gtid-strict-mode.

Added regression test binlog.mdev_29803 that verifies:
- Default (OFF): reading binlog files with replayed events succeeds
- Explicit --gtid-strict-mode: still produces the expected error
Oleksandr Byelkin
Merge branch '10.6' into 10.11
Kristian Nielsen
MDEV-39779: binlog.binlog_gtid_index sporadic failure

The GTID index is written asynchronously from the binlog background thread,
the test would fail when trying to read the index file before the background
thread had time to write it.

Fix by making the test case wait for the file to reach the expected size
before accessing.

Signed-off-by: Kristian Nielsen <[email protected]>
Monty
Update backup code to use new my_dir and my_copy interfaces
Daniel Black
MDEV-40921 Large allocations Use MMAP_NORESERVE (but not large_pages)

The default innodb_buffer_pool_size_max of 8TiB cannot be reserved on
Illumos because anonymous mappings reserve backing store (swap) when they
are created, irrespective of the page protections. Pass MAP_NORESERVE when
reserving the buffer pool address range; swap is still properly reserved,
and out-of-memory reported, when ranges are committed.

commit message by Andy Fiddaman.

On Linux MAP_NORESERVE has similar meaning in that no swap space is
reserved. In the Linux case per manual(mmap), mariadbd may SEGV if there
isn't the swap space available. This quick kill seems preferable to
attempting to run a buffer pool from swap.

Note MAP_NORESERVE isn't used for large pages as we want the allocation
failure to be early. Having failure on a first access here is
unrecoverable while an large page allocation failure can fall back to
a non-large page.

Place -1 ptr constant with MAP_FAILED. Its used elsewhere in code and
matches mmmap documentation.

Other BSDs and MacOS appear to not implement the flag.
Val Doroshchuk
Rename duckdb file name to allow to use duckdb as schema

If duckdb is used as schema, DuckDB requires to use it in queries explicitly since the name conflicts with the catalog.

This fixes
Ambiguous reference to catalog or schema "duckdb" - use a fully qualified path like '.duckdb'
Daniel Black
MDEV-40801 ppc64le ro_after_init isn't pagesize aligned

Align ro_after_init using MAXPAGESIZE instead of COMMONPAGESIZE.

COMMONPAGESIZE may be smaller than the actual page size supported by
the target ABI. This can leave ro_after_init sharing an OS page with
adjacent sections, causing mprotect() to change permissions on data
outside ro_after_init.

Use MAXPAGESIZE so the section boundaries are aligned to the maximum
page size required by the target linker/ABI.

This is particularly important on architectures such as ppc64le and
aarch64, where the runtime page size can differ from COMMONPAGESIZE.

Before:
  .data          0x...1b80000
  ro_after_init  0x...1c70000
  .bss          0x...1c72000

After:
  ro_after_init starts and ends on MAXPAGESIZE boundaries, ensuring
  mprotect() only affects pages belonging to ro_after_init.

Co-authored-by: ChatGPT GPT-5.6 Luna <[email protected]>
Marko Mäkelä
MDEV-40756 Incorrect multi-batch recovery of file size

file_name_t::page0_lsn: Keep track of the last applied
recv_sys_t::parse_page0() so that a multi-batch recovery
will not reset the file to a smaller size.

Reviewed by: Thirunarayanan Balathandayuthapani
Daniel Black
MDEV-40750 gcc-16.1.0 on ppc64 causes innodb to fail to compile

Assembler comes up with the error:
unrecognized opcode: `dcbstps'

dcbstps is a Power 10 instruction. The default target arch on most
platforms is Power 8 or 9.

Added the target power10 to the function pmem_phwsync. The execution
of this function is gated on the ISA 3.1 in pmem_persist_init so
there's no chance of a SIGILL.

clang supports this target as arch=pwr10 and gcc as cpu=power10.
Revert back to using opcodes for old versions.
sjaakola
MDEV-38869 sequence conflicts with streaming replication

Sequence access conflicts with streaming replication could cause the
server to hang, as shown in MDEV-38869.

A sequence table is written from SEQUENCE::next_value() while
SEQUENCE::mutex is held. For a streaming transaction the row write in
handler::ha_write_row() would then replicate a fragment and block waiting
for certification and commit order, while an applier may be waiting for
the same mutex in SEQUENCE::set_value(). Neither side can proceed, the
node deadlocks and the BF abort of the local transaction can never be
delivered.

This commit avoids the deadlock by skipping the streaming replication
step for sequence table rows. The row is already in the write set and is
replicated with the following fragment, or at commit.

Only that one step is skipped. The skip is passed down as a parameter to
wsrep_after_row() and wsrep_after_row_internal() rather than by not
calling them at all, so the row is still counted against
wsrep_max_ws_rows and wsrep_check_pk() still runs. A transaction using
sequences heavily therefore cannot silently exceed the configured write
set row limit.

The commit has also a new mtr test for three sequence/SR conflict
scenarios: galera.galera_sequences_bf_kill_sr

- a streaming transaction and an applier competing for SEQUENCE::mutex,
  where both are expected to commit

- the same, but with the applier also BF aborting the local transaction
  over a gap lock. A streaming transaction cannot be replayed, so it is
  rolled back and the client gets ER_LOCK_DEADLOCK. The applier is held
  at the abort_trx_end sync point until the abort has been issued, so
  that the local transaction cannot finish its fragment first

- twelve row inserts on both nodes with wsrep_trx_fragment_unit=rows, so
  that each node reserves several sequence cache ranges and the sequence
  table writes land inside fragments carrying several rows. The values
  the two nodes hand out must not overlap
Monty
Integrate the old and new backup code

Fixes a lot of issues in current backup code:
- Galera should now be supported (needs testing)
- Enables ddl logging (so we can use it in the future)
- Flushes binary logs (we still must add code to copy them)
- mdl locks are consistent between maria-backup and backup command
- startup backup code for InnoDB moved to innodb_prepare_for_backup()
  called by prepare_for_backup hton handler.
- Give errors if backup command is done under a transaction, global
  read lock or lock tables.
- Retry for MDL_BACKUP_WAIT_DDL (needed for
  backup.backup_ddl_concurrent_verify)
- Removed wrong log locks in Aria

Things to do (in addition to the things in my earlier review) :
- At backup_stage stage start, force rotate of aria log files. This
  allows us to copy all old logs without any locks
- Copy all transactional tables and old aria logs under BACKUP_START (as
- maria-backup does)
- Copy the active aria log file under block commit (Only one file as aria
  log rotation is disabled while backup is running.
- Improve speed of copying aria tables by copy files in up to 1M
  blocks and run checksum on the blocks and only re-read blocks with
-  wrong checksum.
- Copy non transactional files under BACKUP_PHASE_NO_BEGIN_NON_TRANS.
  Note that Aria does not support the documented
  BACKUP_PHASE_NO_DML_NON_TRANS . The BACKUP_PHASE_NO_BEGIN_NON_TRANS
  state is already blocking changes to non transactional tables
Vladislav Vaintroub
Appveyor - post-fix 1a052f2

After 1a052f2 and 1fb0755, disabling RocksDB build via "git config" alone
no longer works. Add -DPLUGIN_ROCKSDB=NO to cmake config line to workaround
Akshat Nehra
MDEV-40867 CONNECT writes unvalidated data from remote filter into fixed-len buffer

TestFil() in storage/connect/tabtbl.cpp uses unbounded sscanf
format specifiers to parse TABID filter values pushed from
ha_connect::CheckCond(). When a WHERE tabname='...' filter
exceeds NAME_LEN bytes (192), sscanf overflows the
stack-allocated tn[NAME_LEN] buffer, corrupting the stack
and crashing mysqld with SIGSEGV.

Fix: add width specifiers to bound all sscanf writes:
- %7s for op[8]
- %192[^'] for tn (NAME_LEN bytes + null terminator)

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.
Yuchen Pei
MDEV-40805 Do not call lock_rec_convert_impl_to_expl if a table S-lock is held

lock_clust_rec_read_check_and_lock() skipped the implicit-to-explicit
conversion only under a table LOCK_X. When a table LOCK_S is held the
conversion is equally pointless: no other transaction can hold an
implicit X-lock on the record, because modifying a row requires a
table LOCK_IX and LOCK_IX is incompatible with our LOCK_S.
lock_table_has() matches stronger modes, so testing LOCK_S subsumes
the old LOCK_X test.
Kristian Nielsen
MDEV-39774: Assertion on slave with binlog_row_image=MINIMAL

When finding the row to modify for a row event, and when not using
rnd_pos_by_record() to locate the row, the code would use
table->use_all_columns(), which makes the read_set and write_set point
to the table->s->all_set in the table share. This caused problems when
other code later modified bits in the read_set or write_set, which
ends up wrongly modifying the table share.

We can just use bitmap_set_all(table->read_set) to mark to read all
columns and leave the possibility to later change the bits as needed.

This code changes in this patch must be null-merged from 10.11 to
11.4, as there the problem is fixed differently.

Signed-off-by: Kristian Nielsen <[email protected]>
drrtuy
MDEV-41001: cpack now skips data path that brings symbolic links to fix Windows build.
drrtuy
fix: MDEV-40846 DuckDB handles functionality that is based on invisible columns, e.g. WITH SYSTEM VERSIONING.
sjaakola
MDEV_38952 Improve galera_sequences family of tests

This commit fixes a sporadic failure with the test case 1,
where recorded result depends on node 1 applying node 2's replicated
sequence update before it resumes its already-open transaction:

- Node 2 SELECT NEXTVAL(s) writes reserved_until=21 and replicates it.
- On node 1 that lands in Rows_log_event::update_sequence()
  Since 21 > next_free_value (9), adjust_values(21) discards node 1's  still-cached value 9.

  Nothing enforced that ordering: node 1's INSERTs run inside BEGIN, and sync wait does not happen mid-transaction.

The fix is to use selarate session, node_1_ctrl, to wait until node 1 has applied the update, before node 1 resumes its transaction
Thirunarayanan Balathandayuthapani
MDEV-40319 Instant ALTER TABLE rollback corrupts virtual column

Problem:
=======
ha_innobase_inplace_ctx::~ha_innobase_inplace_ctx() runs, whenever
ctx->instant_table is set and frees the old_v_cols exist also.
old_v_cols and old_n_v_cols are captured in the constructor as
prebuilt_arg->table->v_cols and n_v_cols, i.e. an alias of the live
table's own virtual columns, not a copy. By the time this destructor
runs, old_table->v_cols is either still that same array. If the
failure happened before ctx->instant_column() ever ran like during
prepare_inplace_alter_table_dict() or failure happened during the
commit phase innobase_instant_try(). In both cases old_v_cols is
the table's current, live v_cols array, so this loop destructs
dict_v_col_t objects that are still in use.

Solution:
========
ha_innobase_inplace_ctx::~ha_innobase_inplace_ctx(): Destruct
instant_table->v_cols[], not old_v_cols[]. instant_table is the
independently allocated dict_table_t that prepare_instant() built;
It owns its own v_cols array, whose dict_v_col_t::v_indexes must
be destructed before dict_mem_table_free() reclaims instant_table's memory.
Luke Lu
MDEV-40385 use-of-uninitialized-value in Binary_string::c_ptr()

SELECT KDF('','',1000,256) triggered an MSAN use-of-uninitialized-value
report in Binary_string::c_ptr() (sql/sql_string.h) reached from
Item_func_kdf::val_str().

The optional 4th argument (kdf_name) was evaluated into a result buffer
and then read as a C string with c_ptr(). When that argument is an
integer literal such as 256, Item_int::val_str() writes the digits "256"
into the buffer without appending a trailing NUL, and String::alloc()
intentionally skips reallocation, so the buffer stays non-"alloced" and
unterminated. c_ptr() then reads Ptr[str_length] to test for an existing
terminator, reading an uninitialized byte. The buffer is a stack-resident
ValueBuffer from Protocol::send_result_set_row, so the byte is validly
addressable but never initialized; only MSAN re-poisons the stack scope,
which is why the report is MSAN-only and Valgrind does not flag it.

Use c_ptr_safe() instead of c_ptr() when reading the kdf_name argument.
c_ptr_safe() writes the NUL terminator after a capacity check without
first reading Ptr[str_length], while c_ptr() reads that byte to detect an
existing terminator. Behaviour is unchanged: a non-matching kdf_name still
yields ER_STD_INVALID_ARGUMENT and NULL, and valid names (pbkdf2_hmac,
hkdf) still work.

The regression cases added to main.func_kdf reproduce the report only
under an MSAN-instrumented build; on a normal build they pass on both the
unfixed and fixed server because the stray read is harmless without a
sanitizer.

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.
Monty
Fixed that translog_walk_filenames() in Aria properly recognized aria
log filenames.
Thirunarayanan Balathandayuthapani
MDEV-26057 Assertion `!vcol->v_indexes.empty() in trx_undo_log_v_idx

Problem:
========
-  Rollback of an INPLACE ALTER TABLE is executed while holding only a
shared metadata lock on the table, so DML can run concurrently.
rollback_inplace_alter_table() resets dict_col_t::ord_part in a
critical section of its own, after row_merge_drop_indexes() already
removed the aborted indexes from the dictionary cache and emptied
dict_v_col_t::v_indexes. During this time, DML statement can see a
virtual column with ord_part set and an empty v_indexes, which
makes assert failure in trx_undo_report_insert_virtual().

Solution:
========
row_merge_reset_ord_part(): Added a function to reset
dict_col_t::ord_part for the columns that are no longer a field of
any index remaining in the dictionary cache.
For virtual columns the decision is based on dict_v_col_t::v_indexes
being empty, and no element is ever removed from that list.

row_merge_drop_indexes(): Added a call to row_merge_reset_ord_part()
in the branch that removes the indexes from the cache, in the same
dict_sys.latch critical section. That branch is taken only when
MDL_EXCLUSIVE is held or when this is the only handle to the table,
so no concurrent DML can observe the intermediate state.
In the lazy drop branch the indexes and their v_indexes entries
stay in the cache and nothing is reset; that is done later,
when the indexes are dropped while holding MDL_EXCLUSIVE.

check_col_exists_in_indexes(): Removed the only_committed parameter,
which no longer has any caller.

row_quiesce_col_ord_part(): Added a function to get
dict_col_t::ord_part and dict_col_t::max_prefix of a column
from the committed indexes that are
present in the dictionary cache.

row_quiesce_write_table(): Write the row_quiesce_col_ord_part() return
values to the .cfg file instead of the cached dict_col_t fields,
because a rolled back ADD INDEX leaves ord_part set until the
aborted index is removed by a later DDL, and
max_prefix is never reset when an index is dropped, which makes
IMPORT TABLESPACE reject the tablespace with a bogus schema mismatch.
drrtuy
feat: MDEV-40672 implement basic support for the pluggable aggregate functions
Kristian Nielsen
MDEV-40575: Sporadic failure of rpl.rpl_gtid_crash

The test fails because the slave is configured in the test with the flaky
--init-rpl-role=slave option by default. As the test case is crashing the
slave at various points, this option occasionally causes the slave to
*truncate* away a transaction during crash recovery, which is surely not
intended for this test.

The use of init-rpl-role=slave by default goes back to 2007(!), when this
option did not have any functionality, and when the option was re-purposed
for the flaky truncate-binlog-at-recovery functionality this default was
overlooked and not removed. The tests that want to test this marginal
functionality should (and do) enable it explicitly.

So remove the use of init-rpl-role=slave by default in the mtr --suite=rpl.

Signed-off-by: Kristian Nielsen <[email protected]>
Oleksandr Byelkin
Merge branch '11.4' into mariadb-11.4.13
Marko Mäkelä
MDEV-40728 Recovery wrongly fails if FILE_CREATE is followed by FILE_RENAME

deferred_spaces.deferred_dblwr(): Skip newly created tablespaces
to avoid a bogus invocation of fil_space_free().

fil_name_process(): Simplify the logic. If no matching tablespace is
found but file_name_t::create_lsn had been set in response to parsing
a FILE_CREATE record, try to apply FILE_RENAME to deferred_spaces.

log_parse_file(): Parse each FILE_ record only once. In multi-batch
recovery, there may be redundant calls that would break the logic of
fil_name_process().
Kristian Nielsen
Merge 10.11 -> 11.4
sjaakola
MDEV-36677 rsync sst fails with different innodb_log_group_home_dir and datadir

Backported the fix done by Pekka Lampio for mariaDB 11.4 in PR
https://github.com/mariadb-corporation/codership-mariadb-server/pull/543

The PR has a fix for wsrep_sst_rsync script and new mtr test:
galera_3nodes.galera_mdev_36677" to check that the rsync SST method of Galera
works correctly also when the joiner node store InnoDB log files in a dedicated
directory separate from the data dictionary

Note: merging this PR to 11.4 may not be fully functional as there are other
changes in the rsync SST script. Take a look at the original 11.4 PR when merging.
Monty
Integrate the old and new backup code

Fixes a lot of issues in current backup code:
- Galera should now be supported (needs testing)
- Enables ddl logging (so we can use it in the future)
- Flushes binary logs (we still must add code to copy them)
- mdl locks are consistent between maria-backup and backup command
- startup backup code for InnoDB moved to innodb_prepare_for_backup()
  called by prepare_for_backup hton handler.
- Give errors if backup command is done under a transaction, global
  read lock or lock tables.
- Retry for MDL_BACKUP_WAIT_DDL (needed for
  backup.backup_ddl_concurrent_verify)
- Removed wrong log locks in Aria

Things to do (in addition to the things in my earlier review) :
- At backup_stage stage start, force rotate of aria log files. This
  allows us to copy all old logs without any locks
- Copy all transactional tables and old aria logs under BACKUP_START (as
- maria-backup does)
- Copy the active aria log file under block commit (Only one file as aria
  log rotation is disabled while backup is running.
- Improve speed of copying aria tables by copy files in up to 1M
  blocks and run checksum on the blocks and only re-read blocks with
-  wrong checksum.
- Copy non transactional files under BACKUP_PHASE_NO_BEGIN_NON_TRANS.
  Note that Aria does not support the documented
  BACKUP_PHASE_NO_DML_NON_TRANS . The BACKUP_PHASE_NO_BEGIN_NON_TRANS
  state is already blocking changes to non transactional tables
Brandon Nesterenko
MDEV-40823: rpl.rpl_queue_event_length_mismatch 'row' fails: Error condition reached in include/wait_for_slave_param.inc

rpl.rpl_queue_event_length_mismatch can sporadically fail at the start
of the second test case, when starting the slave in an expectedly valid
state. This is because the previous test case (1) could not properly
finish cleaning up before it was torn down by the second test case. That
is, the previous test case corrupts a rotate event and ensures the
master catches and errors properly on this corruption. To clean up, test
case 1 resets the debug_dbug state of the master's binlog_dump_thread,
so it can re-send the rotate event that it had previously corrupted.
The test case never actually ensures this rotate event made it to the
slave though. The second test case also arms the binlog_dump_thread to
corrupt a rotate event (but in a different way). If the second test case
stopped the slave before the slave was able to retrieve this corrected
event; the next time the binlog dump thread would start (from test case
2), it would start with a debug_dbug state that would again corrupt this
rotate event (that otherwise should make it problem-free to the slave).
The corrupted rotate event from test case 2 is only meant to apply to
the rotate event *after* FLUSH LOGS.

To fix this, test cases 1 and 2 now end by waiting for the IO thread to
reach the master's position

Signed-off-by: Brandon Nesterenko <[email protected]>
Kristian Nielsen
MDEV-41045: Missing virtual columns in binlog break backwards compat for CDCs

Fix a regression for third-party readers of binlog events, where it was
expected that binlog_row_image=FULL would include also values of virtual
columns.

Virtual column values are not useful on the slave (as they will instead be
re-computed from the other column values), but such recomputation can be
difficult to do outside of the server source. It seems reasonable to allow
these column values to be present in the FULL format (even though they are
redundant), as the FULL format in any case usually contains redundant column
values.

Signed-off-by: Kristian Nielsen <[email protected]>
Oleksandr Byelkin
Merge branch '10.11' into mariadb-10.11.19
Sergei Golubchik
rocksdb: don't abort early in submodule update

Fix for 1fb075512a7aeab8646a163cbb6f265c49f4c075 to allow
the ADD_SUBMODULE to perform updates.
Oleksandr Byelkin
Merge branch '10.11' into 11.4
Vladislav Vaintroub
Rocksdb - suppress MSVC warning in external code

ribbon_impl.h(879,1): warning C4723: potential divide by 0
on VS2025
bsrikanth-mariadb
MDEV-39868 Wrong result with a window fn over merged derived table column

Problem:
========
A query with a window function over a column of a merged derived table
returns an empty set when another table is joined on a condition over the
same column and is accessed with "Range checked for each record":

  SELECT AVG(subq.c2) OVER (), t2.c1
  FROM t1 LEFT JOIN (SELECT * FROM t3) AS subq ON t1.c1 = subq.c1
  STRAIGHT_JOIN t2 ON subq.c2 > t2.c1;

With derived_merge=on all the references to subq.c2 are
Item_direct_view_ref objects sharing one underlying Item_field, because
their ref pointers all point into the derived table's field_translation.
Item::split_sum_func2() calls real_item() and puts that shared Item_field
into the list of the window function's temporary table fields, so
create_tmp_field_from_item_field() sets its result_field to a column of
the temporary table.

Item_field::val_int() reads field, but Item_field::save_in_field() reads
result_field, so the two now return different values. The join condition
is evaluated through the same Item_field, and the runtime range analysis
in Field::get_mm_leaf_int() uses save_in_field_no_warnings(). It reads the
still empty temporary table column instead of the value of t3.c2, treats
the value as NULL, and builds a SEL_TREE::IMPOSSIBLE. Table t2 then
produces no rows.

Solution:
=========
Do not unwrap Item_direct_view_ref in Item::split_sum_func2(). The wrapper
is created per reference and is not shared, so the temporary table field
is attached to the wrapper alone and the conditions that refer to the same
view column keep reading the base table field.

Item_ref::create_tmp_field_ex() already creates the same temporary table
field for a view ref over a column, and change_to_use_tmp_fields() already
handles REF_ITEM, so no other change is needed. Ref access was never
affected: get_store_key() takes real_item()->field explicitly.
Marko Mäkelä
MDEV-15736 fixup: clang -Wunused-but-set-global
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.