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
Georg Richter
Added macro RPL_READ_FIELD_LENGTH which uses net_field_length_safe
Aleksey Midenkov
MDEV-41583 Tests fail on platform-dependent max TIMESTAMP of current row

versioning.partition (MDEV-41543) and main.grant2 (MDEV-41099) print
row_end of the current row. Since MDEV-32188 in 11.5 the max TIMESTAMP
is 2106-02-07 06:28:15.999999 on 64-bit and 2038-01-19 03:14:07.999999
on 32-bit, so the results recorded on 64-bit fail on 32-bit.

The fix prints CURRENT instead of row_end of the current row in
versioning.partition (using current_row_ts() from common.inc) and
replaces row_end with # in main.grant2, where only the privilege to
read it is tested.

Affected since 11.8. The fix is committed to the originating 10.11 to
minimize conflicts.
Aleksey Midenkov
MDEV-40380 vcol.races sporadic timeout

After MDEV-39261 purge opens TABLE via open_purge_table() for any
undo record of a table with indexed virtual columns, including the
insert undo record. If purge does that after FLUSH TABLES, the TABLE
is put back into the table cache. Then DELETE from con1 reuses the
cached TABLE, skips open_table_from_share() and never hits the
TABLE_after_field_clone sync point. DELETE completes immediately,
wait_condition times out and SELECT returns an empty set.

The fix waits for purge to complete before FLUSH TABLES.
Oleksandr Byelkin
MDEV-23864 BULK insert wrong values when command contain inner select

Optimized array binding INSERT/REPLACE executes the statement once,
so subqueries are executed with the first set of parameters only.

Execute INSERT/REPLACE, where a subquery result depends on parameters,
for every set of parameters (as not optimized array binding commands
are executed), keeping the first generated id as the result of the
whole operation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Aleksey Midenkov
MDEV-41583 Tests fail on platform-dependent max TIMESTAMP of current row

versioning.partition (MDEV-41543) and main.grant2 (MDEV-41099) print
row_end of the current row. Since MDEV-32188 in 11.5 the max TIMESTAMP
is 2106-02-07 06:28:15.999999 on 64-bit and 2038-01-19 03:14:07.999999
on 32-bit, so the results recorded on 64-bit fail on 32-bit.

The fix prints CURRENT instead of row_end of the current row in
versioning.partition (using current_row_ts() from common.inc) and
replaces row_end with # in main.grant2, where only the privilege to
read it is tested.

Affected since 11.8. The fix is committed to the originating 10.11 to
minimize conflicts.
Aleksey Midenkov
MDEV-40380 vcol.races sporadic timeout

After MDEV-39261 purge opens TABLE via open_purge_table() for any
undo record of a table with indexed virtual columns, including the
insert undo record. If purge does that after FLUSH TABLES, the TABLE
is put back into the table cache. Then DELETE from con1 reuses the
cached TABLE, skips open_table_from_share() and never hits the
TABLE_after_field_clone sync point. DELETE completes immediately,
wait_condition times out and SELECT returns an empty set.

The fix waits for purge to complete before FLUSH TABLES.
PranavKTiwari
MDEV-25515 User Account Host Names using CIDR notation
CREATE USER u@'192.168.0.0/24';  -- same as '192.168.0.0/255.255.255.0'
CIDR is an input spelling only, normalised to the netmask form while parsing.
A malformed mask is now rejected instead of stored, and one already in
the privilege tables is skipped at load with a warning instead of being honoured.
Nothing rejected these before, and they are not merely
useless: a non-contiguous mask such as 10.0.0.0/255.0.255.0 matches the
scattered set 10.*.0.*.  Skipping rather than erroring keeps such a row
droppable and renamable.
New error ER_INVALID_HOST_NETMASK.
Sergei Golubchik
CONNECT: more FILE and secure_file_priv checks in udfs

Assisted-by: Claude:claude-5.5-opus
PranavKTiwari
MDEV-25515 User Account Host Names using CIDR notation
CREATE USER u@'192.168.0.0/24';  -- same as '192.168.0.0/255.255.255.0'
CIDR is an input spelling only, normalised to the netmask form while parsing.
A malformed mask is now rejected instead of stored, and one already in
the privilege tables is skipped at load with a warning instead of being honoured.
Nothing rejected these before, and they are not merely
useless: a non-contiguous mask such as 10.0.0.0/255.0.255.0 matches the
scattered set 10.*.0.*.  Skipping rather than erroring keeps such a row
droppable and renamable.
New error ER_INVALID_HOST_NETMASK.
Aleksey Midenkov
MDEV-40380 vcol.races sporadic timeout

After MDEV-39261 purge opens TABLE via open_purge_table() for any
undo record of a table with indexed virtual columns, including the
insert undo record. If purge does that after FLUSH TABLES, the TABLE
is put back into the table cache. Then DELETE from con1 reuses the
cached TABLE, skips open_table_from_share() and never hits the
TABLE_after_field_clone sync point. DELETE completes immediately,
wait_condition times out and SELECT returns an empty set.

The fix waits for purge to complete before FLUSH TABLES.
forkfun
MDEV-30295 mysqldump produces syntactically incorrect statement

cover_definer_clause() wrapped DEFINER in "*/ /*!50017 ... */" and broke
when the statement text contained a comment. Dump the server's DDL
as returned.

Also:
- drop /*!NNNNN */ gates below 50700 in mysqldump, mysqlbinlog,
  mysqladmin, mysqlcheck, mysqlshow, mysqlimport, mysqlhotcopy,
  innotop and mtr_warnings.sql; mysqlbinlog's skip_replication
  becomes /*M! ...*/
- SHOW CREATE DATABASE no longer wraps IF NOT EXISTS and DEFAULT
  CHARACTER SET in comments
- remove checks for servers older than 5.7 in mysqldump, mysqlcheck,
  mysql, innotop and the client library, including dump_trigger_old()
  and mysqlcheck's SHOW TABLES fallback
- mysqldump warns on stderr if SHOW CREATE DATABASE fails

Behavior changes:
- --compatible=mysql323 and mysql40 are accepted and ignored. The other
  modes wrap only some SET and ALTER TABLE ... KEYS statements in
  /*!40101 */.
- mysqlhotcopy --old_server is accepted, ignored, and prints a warning.

Assisted-By: Claude:claude-5.5-sonnet
Aleksey Midenkov
MDEV-40380 vcol.races sporadic timeout

After MDEV-39261 purge opens TABLE via open_purge_table() for any
undo record of a table with indexed virtual columns, including the
insert undo record. If purge does that after FLUSH TABLES, the TABLE
is put back into the table cache. Then DELETE from con1 reuses the
cached TABLE, skips open_table_from_share() and never hits the
TABLE_after_field_clone sync point. DELETE completes immediately,
wait_condition times out and SELECT returns an empty set.

The fix waits for purge to complete before FLUSH TABLES.
Sergei Petrunia
Add comments, Don't remove from THD::free_list.
Alessandro Vetere
MDEV-41398 Stale optimistic btr_pcur_t restore after buffer pool resize

Problem: btr_pcur_t::restore_position() accepts a stored position
optimistically when the block at the stored descriptor address has the
same page id and the same buf_block_t::modify_clock. When
buf_pool_t::resize() shrinks the buffer pool, the withdrawn block
descriptors are freed. When it grows the buffer pool again, the new
descriptors at the same addresses start with modify_clock=0. A cursor
that was stored before the shrink can then find a different instance of
the page with an equal modify_clock, and it restores to a record that
is no longer correct. In a release build, an UPDATE can change a row
that does not match its WHERE condition and skip the rows that match.

Fix: Add buf_pool_t::withdrawn_modify_clock. buf_pool_t::shrink() sets
it to the maximum modify_clock of the blocks that it withdraws.
buf_pool_t::resize() sets the initial modify_clock of each block that
it adds to one more than this. A relocation in shrink() increments only
the modify_clock of the destination block, so a withdrawn block can
keep the value that a cursor stored. Thus the modify_clock at a block
descriptor address never repeats an earlier value, and a stale
optimistic restore fails. The new member uses a hole before
buf_pool_t::flush_list_mutex, so the layout of the other members does
not change.

The test innodb.buf_pool_resize_pcur_restore reads a page into the
first new block after a grow, and makes a stored cursor for a read and
for an UPDATE restore its position across a page reorganization, a
shrink and a grow.
forkfun
MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads

Event_job_data::execute() pointed thd->security_ctx at a local
Security_context. A concurrent PROCESSLIST scan captures that pointer
without a lock and can read it after execute() returns.

Make the Security_context a member of Event_job_data, which lives in
Event_worker_thread::run() until after thd is deleted.

Add main.events_debug test
Raghunandan Bhat
MDEV-34817: Performance schema is not cleared of removed package routines- part 2

Problem:
  1. Package routines were instrumented as "<package>.<routine>", the
  same key as a standalone routine named `<package>.<routine>`. Their
  statistics were merged, and DROP PACKAGE BODY dropped the statistics
  of standalone routines named `<package>.<anything>`.

  2. Routines of a package with a name of COL_OBJECT_NAME_SIZE bytes or
  longer were truncated to "<package>" and were never dropped.

Solution:
  Instrument package routines with internal object types
  OBJECT_TYPE_PACKAGE_PROCEDURE and OBJECT_TYPE_PACKAGE_FUNCTION. They
  are used only in the hash key: SETUP_OBJECTS lookups and OBJECT_TYPE
  columns still use the old PROCEDURE and FUNCTION. When a package body
  is dropped, drop only records with these types, including names
  truncated to the package name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Alessandro Vetere
MDEV-32286 Reuse remembered clustered leaves in secondary-index scans

Row_sel_get_clust_rec_for_mysql::operator() descends the clustered B-tree
from the root for every row whose clustered-index record a secondary-index
scan must read, although consecutive rows land on the same clustered leaf
page wherever the secondary order tracks the clustered one. A non-covering
scan needs one for every row, a locking read needs one whatever the
secondary index holds, because an exclusive select lock type makes
ha_innobase::build_template() build its template against the clustered
index, and a covering scan needs one for every row of a secondary leaf
whose PAGE_MAX_TRX_ID its read view cannot see. ANALYZE FORMAT=JSON
charges each descent its full height, and those descents are nearly the
whole cost: the secondary index is charged its own descent and one page
for each further leaf, and nothing per row, because the position that its
cursor holds between two rows is restored optimistically, which latches
the leaf again without counting an access. So pages_accessed is the row
count times the height of the clustered index, plus a handful: 1000 rows
over a 2-level clustered index cost 2006 and 750 rows over a 3-level one
cost 2291, where a full table scan of the same data costs 23 and 110.

Let a handle remember the clustered leaves that the lookups of one
statement reached, and let the next lookup try them before it descends
again. The reasoning behind each value and each rejection is in the
comments beside it.

row0mysql.h defines clust_leaf_hint_slot, which names one leaf: its page
number, copies of its first and last user record truncated to the key
fields, which bound the key range that the leaf held when it was
remembered, and the rec_get_offsets() of both. A slot of a
leaf that had no right sibling names no last key, because every key above
the last record of the rightmost leaf still belongs to it.
CLUST_LEAF_HINT_SLOTS (4) slots hang off the new
row_prebuilt_t::clust_leaf_hint, beside clust_leaf_hint_mru, the most
recently used order held as slot numbers, and clust_leaf_hint_n and
clust_leaf_hint_miss, the used-slot count and the miss counter.

row0sel.cc holds the policy. row_sel_clust_leaf_hint_covers() compares a
key against the remembered ranges, so a lookup that no slot can answer
costs no buffer pool access and no pages_accessed.
row_sel_clust_leaf_hint_search() probes the first slot that covers the key
and moves it to the front of the order.
row_sel_clust_leaf_hint_remember() records the leaf that a descent landed
on, and refreshes the slot of a leaf that is remembered already rather
than spend a second one on the same page. Two descents fill no slot: the
lookups of a statement before lookup CLUST_LEAF_HINT_MIN_LOOKUPS (4), and
a leaf that is the root. row_sel_clust_leaf_hint_armed() stands a scan
down once the slots stop paying for themselves: a miss adds
CLUST_LEAF_HINT_MISS_WEIGHT (2) to the miss counter and a hit takes one
away, so a scan gives the slots up where it answers too little of its
lookups to pay for them, CLUST_LEAF_HINT_MAX_MISSES (8) misses with no hit
between them still reach the threshold, and one lookup in
CLUST_LEAF_HINT_RETRY (1024) starts the count again, so a scan whose order
becomes correlated only later recovers. Both halves of the cost stop
there, the test of the slots and the copies that refresh them.
Row_sel_get_clust_rec_for_mysql::operator() calls all of this in place of
its btr_pcur_open_with_no_init(), and only where the adaptive hash index
is disabled. A guess of that index lands on the record with no page-local
search and no page access to charge, but neither of the two is faster for
every shape of index and scan, and a hint that runs first stops that index
from adding entries. The adaptive hash index is off by default, so the
hints are active in a default configuration.

btr0cur.h and btr0cur.cc add btr_cur_t::try_leaf_hint(), a PAGE_CUR_LE,
BTR_SEARCH_LEAF search on one named leaf, which starts from the record
where the last search of that leaf landed while that record is still valid,
and from a binary search of the page otherwise. It acquires the page with
buf_page_try_get(): a hint is never derived from a latched parent page,
so by the time it is tried it can precede the caller's already-latched
secondary-index leaf in the latching order, where a blocking wait can
deadlock. It then rejects the page unless the checks that it makes on the
latched frame put the match on it. Those checks are the sole authority on
the result, so a stale range costs a wasted probe or a needless descent,
never a wrong result, and the ranges need no invalidation protocol.

A slot also holds a btr_leaf_step, where the last search of its leaf
landed: the block, its buf_block_t::modify_clock() and the record. A
record pointer stays valid while the block holds the same page and the
clock has not moved, because eviction, deletion and reorganization
advance the clock and an insertion moves no record. While the step is
valid, btr_cur_t::try_leaf_hint() starts from that record instead of a
binary search of the page. page0cur.cc adds page_cur_search_near(),
which compares the key with the record and with one neighbour, and
positions the cursor on the record, on the record after it or on the
record before it. It refuses any other answer and leaves it to
page_cur_search_with_match(). So a scan whose consecutive rows are
neighbours in the clustered index finds each row with two comparisons. A
scan that reads the clustered leaf in falling key order does the same: a
secondary key that falls as the primary key rises, or a secondary index
read backward. Where the secondary order tracks the clustered one with
gaps between the rows, the step fails, and btr_leaf_step::expect (below)
leaves the leaf to the binary search. The step refuses the metadata
pseudo-record, which compares below every key on 0 fields, and a record
before it that is the infimum. A debug build checks every successful
step against the binary search.

A failed step costs comparisons whose outcome no branch predictor can
guess, so btr_leaf_step::expect gates it. The next search tries the step
only where this one landed where a step would have: on the remembered
record, or right before or after it. The successor link of the landing
record tells the record right before, because finding a predecessor takes
a walk of the page directory. So a scan in random order over a few leaves,
which the slots answer in full, keeps the binary search and pays only the
tests that set expect. row_sel_clust_leaf_hint_remember() sets the step on
the record where a descent landed, with expect set.

ha_innodb.cc: ha_innobase::reset() zeroes the used-slot count and the miss
counter per statement, matching autoinc_last_value. row0mysql.cc:
row_prebuilt_free() frees the key buffers that the slots own.

innodb.clust_leaf_hint measures pages_accessed over key orders that differ
in how closely the secondary order tracks the clustered one, and further
tables check query results over the record formats and key shapes
that a clustered-index lookup has to read, down to the metadata
pseudo-record of instant ALTER TABLE, to leaves that split and merge while
a locking read walks them, and to a record that a remembered leaf supplies
for a scan that must then rebuild an older version of it. Two of the
tables scan a covering index, which reads a clustered record under an
exclusive select lock type and under a PAGE_MAX_TRX_ID that the read view
cannot see. clust_leaf_hint_off_debug runs the same body with the hints
turned off, through a debug switch that returns before a lookup tests or
refreshes the slots, so a diff of the two .result files is what the hints
save: 2006 to 1031 (2-level clustered index), 2291 to 1011 (3-level), 4006
to 2015 (two interleaved key ranges), 12016 to 9078 (locality in the
second half alone) and 20020 to 10045 for a covering scan that FOR UPDATE
makes non-covering, where the same scan without FOR UPDATE costs 20 in
both files. Two orders with too little locality to pay for the slots give
them up early and end within a hundred accesses of the unhinted count:
20020 to 19966 (decorrelated) and 20020 to 19999 (shuffled).

Two further tables in innodb.clust_leaf_hint cover the step back. t16
scans a secondary index whose keys fall as the primary key rises, and
reads an ascending secondary index backward: 20020 to 10047 for both. t17
runs the inverse scan on ROW_FORMAT=REDUNDANT with an instantly added
column. innodb.clust_leaf_hint_backward_debug reaches the two refusals of
the step back that no count shows, at the infimum and at the metadata
pseudo-record. A lookup reaches either only for a key that left the
clustered index while the slot still covers it. The rollback of an insert
over a delete-marked record removes the key while purge leaves the
secondary entry, and a PAGE_MAX_TRX_ID that the read view cannot see makes
the reader look that entry up. A debug switch writes each refusal to the
error log, and the case reads both back.

A slot stays valid when dict_index_t::clear_instant_alter() changes the
index under a reader that holds a shared metadata lock, between two
lookups or during one. That clear runs where purge or rollback removes
the last user record of a single-page table whose only instant change was
ADD COLUMN, and it raises dict_index_t::n_core_fields.
rec_copy_prefix_to_buf() writes the copies in the format of an index
without instant ADD COLUMN, their offsets are made with them, and the
comparisons depend on the key fields alone, which no instant change moves.
So the slots do not record n_core_fields, and the hints read it only under
a latch on a clustered leaf.

innodb.clust_leaf_hint_instant_alter makes a lookup compare against a slot
that a clear has outlived. The clear needs the clustered index to lose the
last user record of its root page, while no leaf that is the root fills a
slot, so the tree has to shrink between the two, which purge does there.
The reader therefore reads uncommitted rows at READ UNCOMMITTED and waits
in a stored function while a rollback and purge take them away, and one
row that arrives above the position it stopped at is the lookup that
tests the slots.

main.rowid_filter_innodb: 90 to 88, and its ahi combination unchanged.

A static_assert in Row_sel_get_clust_rec_for_mysql::operator() fails to
compile if btr_search.enabled stops being a Boolean that applies to every
index, because the test !btr_search.enabled that stands the hints down
would then have to become !btr_search.is_enabled(clust_index).
Dmitry Shulga
MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }

Extended the system table mysql.event to support creation of
system triggers (startup, shutdown, logon, logoff) and ddl triggers.

The system table mysql.event is extended with three columns
  `kind`, `when`, `ddl_type`.
For the task MDEV-30645 only the column `kind` is required,
the other two columns are required for the tasks
  MENT-2355, MENT-2291
but since it is better to reduce a number of times the system table
is changed and as a consequences a number of upgrades to be run,
the entire set of columns is added at once.

Type of the columns `kind` and `ddl_type` are specified as SET to allow
storing of triggers that handle several events.
Sergei Golubchik
CONNECT udfs crash on long filename

namely, json_get_item, jsonget_string, json_set_item, jbin_set_item,
bson_get_item, bsonget_string, bson_set_item

Assisted-by: Claude:claude-5.5-opus
mariadb-PranavTiwari
MDEV-37719: mysql_create_like_table() kept a stale TABLE pointer in table->table after OR REPLACE dropped the old table
1- temporary target: drop_temporary_table() frees the old TABLE but
  table->table was left pointing at it, so the table->table block
  that picks up the new table never ran and the binlog branch
  dereferenced freed memory. Reset table->table after the create so
  the new TABLE is used.
2- locked tables: if reopen_tables() failed, the function continued
  into the binlog branch with the freed pointer. Leave via err: instead.

Note- Remove this fix when merging into main.
PranavKTiwari
MDEV-25515 User Account Host Names using CIDR notation
CREATE USER u@'192.168.0.0/24';  -- same as '192.168.0.0/255.255.255.0'
CIDR is an input spelling only, normalised to the netmask form while parsing.
A malformed mask is now rejected instead of stored, and one already in
the privilege tables is skipped at load with a warning instead of being honoured.
Nothing rejected these before, and they are not merely
useless: a non-contiguous mask such as 10.0.0.0/255.0.255.0 matches the
scattered set 10.*.0.*.  Skipping rather than erroring keeps such a row
droppable and renamable.
New error ER_INVALID_HOST_NETMASK.
Alessandro Vetere
MDEV-32286 Reuse remembered clustered leaves in secondary-index scans

Row_sel_get_clust_rec_for_mysql::operator() descends the clustered B-tree
from the root for every row whose clustered-index record a secondary-index
scan must read, although consecutive rows land on the same clustered leaf
page wherever the secondary order tracks the clustered one. A non-covering
scan needs one for every row, a locking read needs one whatever the
secondary index holds, because an exclusive select lock type makes
ha_innobase::build_template() build its template against the clustered
index, and a covering scan needs one for every row of a secondary leaf
whose PAGE_MAX_TRX_ID its read view cannot see. ANALYZE FORMAT=JSON
charges each descent its full height, and those descents are nearly the
whole cost: the secondary index is charged its own descent and one page
for each further leaf, and nothing per row, because the position that its
cursor holds between two rows is restored optimistically, which latches
the leaf again without counting an access. So pages_accessed is the row
count times the height of the clustered index, plus a handful: 1000 rows
over a 2-level clustered index cost 2006 and 750 rows over a 3-level one
cost 2291, where a full table scan of the same data costs 23 and 110.

Let a handle remember the clustered leaves that the lookups of one
statement reached, and let the next lookup try them before it descends
again. The reasoning behind each value and each rejection is in the
comments beside it.

row0mysql.h defines clust_leaf_hint_slot, which names one leaf: its page
number, copies of its first and last user record truncated to the key
fields, which bound the key range that the leaf held when it was
remembered, and the rec_get_offsets() of both. A slot of a
leaf that had no right sibling names no last key, because every key above
the last record of the rightmost leaf still belongs to it.
CLUST_LEAF_HINT_SLOTS (4) slots hang off the new
row_prebuilt_t::clust_leaf_hint, beside clust_leaf_hint_mru, the most
recently used order held as slot numbers, and clust_leaf_hint_n and
clust_leaf_hint_miss, the used-slot count and the miss counter.

row0sel.cc holds the policy. row_sel_clust_leaf_hint_covers() compares a
key against the remembered ranges, so a lookup that no slot can answer
costs no buffer pool access and no pages_accessed.
row_sel_clust_leaf_hint_search() probes the first slot that covers the key
and moves it to the front of the order.
row_sel_clust_leaf_hint_remember() records the leaf that a descent landed
on, and refreshes the slot of a leaf that is remembered already rather
than spend a second one on the same page. Two descents fill no slot: the
lookups of a statement before lookup CLUST_LEAF_HINT_MIN_LOOKUPS (4), and
a leaf that is the root. row_sel_clust_leaf_hint_armed() stands a scan
down once the slots stop paying for themselves: a miss adds
CLUST_LEAF_HINT_MISS_WEIGHT (2) to the miss counter and a hit takes one
away, so a scan gives the slots up where it answers too little of its
lookups to pay for them, CLUST_LEAF_HINT_MAX_MISSES (8) misses with no hit
between them still reach the threshold, and one lookup in
CLUST_LEAF_HINT_RETRY (1024) starts the count again, so a scan whose order
becomes correlated only later recovers. Both halves of the cost stop
there, the test of the slots and the copies that refresh them.
Row_sel_get_clust_rec_for_mysql::operator() calls all of this in place of
its btr_pcur_open_with_no_init(), and only where the adaptive hash index
is disabled. A guess of that index lands on the record with no page-local
search and no page access to charge, but neither of the two is faster for
every shape of index and scan, and a hint that runs first stops that index
from adding entries. The adaptive hash index is off by default, so the
hints are active in a default configuration.

btr0cur.h and btr0cur.cc add btr_cur_t::try_leaf_hint(), a PAGE_CUR_LE,
BTR_SEARCH_LEAF search on one named leaf, which starts from the record
where the last search of that leaf landed while that record is still valid,
and from a binary search of the page otherwise. It acquires the page with
buf_page_try_get(): a hint is never derived from a latched parent page,
so by the time it is tried it can precede the caller's already-latched
secondary-index leaf in the latching order, where a blocking wait can
deadlock. It then rejects the page unless the checks that it makes on the
latched frame put the match on it. Those checks are the sole authority on
the result, so a stale range costs a wasted probe or a needless descent,
never a wrong result, and the ranges need no invalidation protocol.

A slot also holds a btr_leaf_step, where the last search of its leaf
landed: the block, its buf_block_t::modify_clock() and the record. A
record pointer stays valid while the block holds the same page and the
clock has not moved, because eviction, deletion and reorganization
advance the clock and an insertion moves no record. While the step is
valid, btr_cur_t::try_leaf_hint() starts from that record instead of a
binary search of the page. page0cur.cc adds page_cur_search_near(),
which compares the key with the record and with one neighbour, and
positions the cursor on the record, on the record after it or on the
record before it. It refuses any other answer and leaves it to
page_cur_search_with_match(). So a scan whose consecutive rows are
neighbours in the clustered index finds each row with two comparisons. A
scan that reads the clustered leaf in falling key order does the same: a
secondary key that falls as the primary key rises, or a secondary index
read backward. Where the secondary order tracks the clustered one with
gaps between the rows, the step fails, and btr_leaf_step::expect (below)
leaves the leaf to the binary search. The step refuses the metadata
pseudo-record, which compares below every key on 0 fields, and a record
before it that is the infimum. A debug build checks every successful
step against the binary search.

A failed step costs comparisons whose outcome no branch predictor can
guess, so btr_leaf_step::expect gates it. The next search tries the step
only where this one landed where a step would have: on the remembered
record, or right before or after it. The successor link of the landing
record tells the record right before, because finding a predecessor takes
a walk of the page directory. So a scan in random order over a few leaves,
which the slots answer in full, keeps the binary search and pays only the
tests that set expect. row_sel_clust_leaf_hint_remember() sets the step on
the record where a descent landed, with expect set.

ha_innodb.cc: ha_innobase::reset() zeroes the used-slot count and the miss
counter per statement, matching autoinc_last_value. row0mysql.cc:
row_prebuilt_free() frees the key buffers that the slots own.

innodb.clust_leaf_hint measures pages_accessed over key orders that differ
in how closely the secondary order tracks the clustered one, and further
tables check query results over the record formats and key shapes
that a clustered-index lookup has to read, down to the metadata
pseudo-record of instant ALTER TABLE, to leaves that split and merge while
a locking read walks them, and to a record that a remembered leaf supplies
for a scan that must then rebuild an older version of it. Two of the
tables scan a covering index, which reads a clustered record under an
exclusive select lock type and under a PAGE_MAX_TRX_ID that the read view
cannot see. clust_leaf_hint_off_debug runs the same body with the hints
turned off, through a debug switch that returns before a lookup tests or
refreshes the slots, so a diff of the two .result files is what the hints
save: 2006 to 1031 (2-level clustered index), 2291 to 1011 (3-level), 4006
to 2015 (two interleaved key ranges), 12016 to 9078 (locality in the
second half alone) and 20020 to 10045 for a covering scan that FOR UPDATE
makes non-covering, where the same scan without FOR UPDATE costs 20 in
both files. Two orders with too little locality to pay for the slots give
them up early and end within a hundred accesses of the unhinted count:
20020 to 19966 (decorrelated) and 20020 to 19999 (shuffled).

Two further tables in innodb.clust_leaf_hint cover the step back. t16
scans a secondary index whose keys fall as the primary key rises, and
reads an ascending secondary index backward: 20020 to 10047 for both. t17
runs the inverse scan on ROW_FORMAT=REDUNDANT with an instantly added
column. innodb.clust_leaf_hint_backward_debug reaches the two refusals of
the step back that no count shows, at the infimum and at the metadata
pseudo-record. A lookup reaches either only for a key that left the
clustered index while the slot still covers it. The rollback of an insert
over a delete-marked record removes the key while purge leaves the
secondary entry, and a PAGE_MAX_TRX_ID that the read view cannot see makes
the reader look that entry up. A debug switch writes each refusal to the
error log, and the case reads both back.

A slot stays valid when dict_index_t::clear_instant_alter() changes the
index under a reader that holds a shared metadata lock, between two
lookups or during one. That clear runs where purge or rollback removes
the last user record of a single-page table whose only instant change was
ADD COLUMN, and it raises dict_index_t::n_core_fields.
rec_copy_prefix_to_buf() writes the copies in the format of an index
without instant ADD COLUMN, their offsets are made with them, and the
comparisons depend on the key fields alone, which no instant change moves.
So the slots do not record n_core_fields, and the hints read it only under
a latch on a clustered leaf.

innodb.clust_leaf_hint_instant_alter makes a lookup compare against a slot
that a clear has outlived. The clear needs the clustered index to lose the
last user record of its root page, while no leaf that is the root fills a
slot, so the tree has to shrink between the two, which purge does there.
The reader therefore reads uncommitted rows at READ UNCOMMITTED and waits
in a stored function while a rollback and purge take them away, and one
row that arrives above the position it stopped at is the lookup that
tests the slots.

main.rowid_filter_innodb: 90 to 88, and its ahi combination unchanged.

A static_assert in Row_sel_get_clust_rec_for_mysql::operator() fails to
compile if btr_search.enabled stops being a Boolean that applies to every
index, because the test !btr_search.enabled that stands the hints down
would then have to become !btr_search.is_enabled(clust_index).
Marko Mäkelä
pread_write(): Copy 1MiB at a time, and invoke Linux readahead(2)
Oleksandr Byelkin
MDEV-41502 Bulk INSERT: IN list and REGEXP with a parameter use the first parameter set for all rows

Optimized array binding INSERT/REPLACE fixes the items once, and parameters
are constants, so items cache values computed with the first set of
parameters (IN array, compiled REGEXP pattern).

Do not treat parameters as constants during optimized array binding
execution.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Marko Mäkelä
fixup! c98b67beaaedf0bf72d036e1489daf465abf0b4c

Do not duplicate queue in ctx.garbage_lsn
Alessandro Vetere
Fix the build with cmake -DWITH_INNODB_AHI=OFF

Without BTR_CUR_HASH_ADAPT, buf_block_t::index, btr_search and
innodb_adaptive_hash_index_cells do not exist, so the assertion in
buf_LRU_truncate_temp() and the derivation of btr_search.n_cells in
innodb_init_params() are compiled only with it.

mysql-test/collections/broken-no-innodb-ahi.list names the tests
that fail in such a build, for --skip-test-list. They need the adaptive
hash index variables or counters, or the IS_HASHED column of
INFORMATION_SCHEMA.INNODB_BUFFER_PAGE and INNODB_BUFFER_PAGE_LRU, which
the build omits together with the sys schema views that read it.
Aleksey Midenkov
MDEV-39306 Comments on update_virtual_fields() modes
Oleksandr Byelkin
MDEV-34817 Performance schema is not cleared of removed package routines

Package routines are recorded in performance schema as
PROCEDURE/FUNCTION "<package>.<routine>", but dropping a package body
removed only the PACKAGE BODY record, so routine statistics survived
DROP PACKAGE [BODY] and DROP DATABASE.

Drop the routine records of the package together with the package body
record. Also reset statistics of a newly allocated PFS_program (as in
089caf901f9), so a reused slot does not inherit stale counters.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
PranavKTiwari
MDEV-25515 User Account Host Names using CIDR notation
CREATE USER u@'192.168.0.0/24';  -- same as '192.168.0.0/255.255.255.0'
CIDR is an input spelling only, normalised to the netmask form while parsing.
A malformed mask is now rejected instead of stored, and one already in
the privilege tables is skipped at load with a warning instead of being honoured.
Nothing rejected these before, and they are not merely
useless: a non-contiguous mask such as 10.0.0.0/255.0.255.0 matches the
scattered set 10.*0.*. Skipping rather than erroring keeps such a row droppable and renamable, except for a literal CIDR host stored by an older version (for example 10.0.0.0/24): the parser converts that name, so it can no longer be named and must be removed with a direct DELETE from the privilege tables.

New error ER_INVALID_HOST_NETMASK, ER_ACL_ENTRY_IGNORED_BAD_HOST_MASK
Mohammad Tafzeel Shams
MDEV-26123: Always mark spatial index reads as locking

A SELECT query could fail with ER_READ_ONLY_TRANSACTION when search
on spatial index is done after search on non-spatial index.

R-tree index search always require page locks. Previously,
trx->will_lock was only set for spatial indexes when the transaction
had not yet been started. If a transaction was already active due to
a prior non-spatial index access, index_read() for spatial index
returned HA_ERR_READ_ONLY_TRANSACTION instead of marking the transaction
as locking.

trx->will_lock must not be changed once a transaction has started, so
index_read() cannot safely fix this up by itself.

Fix:
- ha_innobase::external_lock() : Set trx->will_lock before the
transaction starts for any table with a spatial index
(dict_table_t::has_spatial_index()), so this no longer depends on
statement/index access order.
- ha_innobase::index_read() : Drop the HA_ERR_READ_ONLY_TRANSACTION
error; trx->will_lock is only set here as a pre-start fallback.
Aleksey Midenkov
MDEV-39306 Index corruption on UPDATE when using versioned InnoDB table (on UPDATE)

Duplicated by:

MDEV-39711 UPDATE on System-Versioned Tables uses the after-value
          in Row-format Binlog's before-image
MDEV-24646 ASAN heap-use-after-free in Field_blob::pack /
          THD::binlog_update_row upon DML on table with virtual column

vers_update_fields() recomputed virtual fields with VCOL_UPDATE_FOR_READ.
For BLOB virtual columns this mode swaps Field_blob::value and read_value
after the computation (swap_values). After the swap, Field_blob::value
holds the buffer that record[1] (the old row image) points to. The next
VCOL_UPDATE_FOR_WRITE computation, done by fill_record() right after
vers_update_fields(), stores the new value into that buffer and so
overwrites the old row image. As a result, InnoDB builds the old
secondary-index key from the new value (MDEV-39306), and the row-format
binlog writes the new value into the before-image (MDEV-39711), or a
freed buffer (MDEV-24646).

The fix removes the virtual fields computation from vers_update_fields().
Its callers now recompute virtual fields with VCOL_UPDATE_FOR_WRITE after
the system fields are set: fill_record() already did, while
update_generated_fields(), multi_update::do_updates() and the RBR
appliers do it explicitly. multi_update::do_updates() still computes
virtual fields twice per row: before BEFORE triggers and after
vers_update_fields(), since the system fields cannot be set before
compare_record().

Based on the MDEV-24646/MDEV-39711 fix by ParadoxV5 (0c2f3de1a2d),
which switched vers_update_fields() to VCOL_UPDATE_FOR_WRITE.
Sergei Golubchik
MDEV-41613 CONNECT jfile_convert crashes on invalid file

Assisted-by: Claude:claude-5.5-opus
Georg Richter
Infer fixes (dead code)
Georg Richter
CONC-861: Bounds checks for length-encoded integers and binary rows

Add net_field_length_safe()/net_field_length_ll_safe(), which take an end
pointer, reject 0xFF, truncated prefixes and 32-bit overflow, and
optionally reject the NULL marker (MARIADB_LENENC_FORBID_NULL).

Export mariadb_net_field_length() (version node libmariadb_3_3_22).

Use the checked decoders in cli_report_progress(),
mthd_my_read_query_result(), mthd_my_read_one_row(),
mysql_list_processes(), mthd_stmt_read_all_rows(),
mthd_stmt_flush_unbuffered() and mthd_stmt_fetch_to_bind().

mthd_stmt_fetch_to_bind() now owns the column boundary: it validates the
length prefix and the lengths of temporal types, and resyncs the row
pointer after callbacks and conversion functions.

Behavior changes: malformed text rows report CR_MALFORMED_PACKET instead
of CR_UNKNOWN_ERROR; EOF packets shorter than 5 bytes are rejected.

Assisted by Claude
Marko Mäkelä
Give better posix_fadvise
Dmitry Shulga
MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }

This patch add support of system triggers fired on server start up
and shutdown. On start up triggers can be associated only with AFTER
event time, on shutdown triggers associated with BEFORE event time.
Several triggers can be created to be run on the same time/event type.

Core implementation
- Extended the sql grammar to support creation of system triggers
  on startup/shutdown
- System triggers metadata is stored in the table mysql.event
- Events and system triggers share the same name space since both are stored
  in the table mysql.event declared with the PRIMARY KEY (`db`,`name`).
  In result, attempt to create an event with the same name as existent
  trigger and vice versa results in failure with the new error
  ER_TRG_EVENT_CONFLICTS_NAME
- Since system triggers metadata is stored in the mysql.event table,
  the table mysql.event is now opened on handling the DROP TRIGGER
  statement
- In case server is started against old version of mysql.event table
  that missing columns required for support of system triggers, system trigger
  feature is disabled
- System triggers are loaded from the table mysql.event right before
  their execution
- System triggers can be created only by users with the SUPER privilege
- Added the option --sys_triggers for mysqldump to dump system triggers.
  The option is turned off by default. In case this option is on, mysqldump
  first dump the table 'mysql' unconditionally, regardless whether
  it was listed in the option --ignore-database
Aleksey Midenkov
MDEV-41583 Tests fail on platform-dependent max TIMESTAMP of current row

versioning.partition (MDEV-41543) and main.grant2 (MDEV-41099) print
row_end of the current row. Since MDEV-32188 in 11.5 the max TIMESTAMP
is 2106-02-07 06:28:15.999999 on 64-bit and 2038-01-19 03:14:07.999999
on 32-bit, so the results recorded on 64-bit fail on 32-bit.

The fix prints CURRENT instead of row_end of the current row in
versioning.partition (using current_row_ts() from common.inc) and
replaces row_end with # in main.grant2, where only the privilege to
read it is tested.

Affected since 11.8. The fix is committed to the originating 10.11 to
minimize conflicts.
Hemant Dangi
MDEV-41414: WSREP Binlog TC: analysis & design

Commits of replicated write sets must finish in the same seqno order
on every node, with or without a binlog. Make the server enforce that
order, so binlog group commit, DDL, views and state transfer no longer
depend on the commit monitor in the Galera library.

The library registers a ticket for every certified write set. The new
Commit_order_manager in the server makes each commit wait for the one
before it, hands the turn on, tracks the committed position, and
handles aborts, node failure and commits that never finish.