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
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Georg Richter
CONC-870: Fix stack buffer overflow in ma_fcvt()

ma_fcvt() had no destination-size parameter. Its zero-padding loop
writes bytes proportional to the input value's magnitude, not its
precision, so a large-magnitude double overflowed the fixed 300-byte
buffer used by convert_from_float()/convert_from_double() in
ma_stmt_codec.c (e.g. 1e308 with precision=30 needs 341 bytes).

Add a to_size parameter to ma_fcvt() and bound every write, mirroring
the existing pattern in ma_gcvt(). Update both call sites.

Added test_ma_fcvt_bounded, covering the overflow scenario plus the
decpt<=0 and DTOA_OVERFLOW branches directly.
  • win-connector_c_3: 'dojob cd win32 && del CMakeCache.txt && cmake --version && cmake ..\src -G "Visual Studio 17 2022" -A "Win32" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SSL=SCHANNEL -DWITH_MSI=ON -DCURL_INCLUDE_DIR=c:\tools\curl\include && cmake --build . --clean-first --config RelWithDebInfo --target package' failed -  stdio
Oleksandr Byelkin
MDEV-41738 MEMORY BTREE: DELETE by key prefix skips rows

Regression from MDEV-40186: for unique BTREE keys heap_rkey/rnext/rprev
updated lastkey_len but not lastkey, so a scan restarted after DELETE
searched from a stale key.

Always save the found key in lastkey for BTREE indexes.

Assisted-By: Claude Opus 5.5 <[email protected]>
Alessandro Vetere
MDEV-39092 BACKUP SERVER: copy Aria tables from the open files

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

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

Assisted-by: Claude:claude-opus-5-5
Nikita Malyavin
MDEV-39927 Multi-key read: batch index condition pushdown for non-parametrizeable series of ranges

A join that reads a local table first and looks up a remote table by key
pays for the remote table in round trips or in rows. Remote means that
the engine keeps its data on other machines, so every request is a
network round trip, and that it can evaluate a condition next to the
data. Ref access sends one request per local row. BKA sends one MRR
request for all buffered keys, but the engine returns every row that
shares a key, and the server filters them by the local values
afterwards. In

    SELECT w.sym, t.id, t.ts, t.price
      FROM watch w STRAIGHT_JOIN trades t
        ON t.sym = w.sym AND t.ts >= w.since AND t.price > w.min_price

with 4 local watch rows, 200 trades rows share a sym with them and 10
satisfy the ON clause: BKA returns 200 rows, where an engine that
evaluated the condition would return 10.

No pushdown interface allows this, because none gives the engine a
condition over the local values together with the values of every
buffered row. cond_push sees only the table's own columns. Under BKA the
server keeps an index condition over other tables to itself and
evaluates it on each fetched tuple (cache_idx_cond, skip_index_tuple).
Pushdown of a select or a derived table to the handlerton would make the
engine read the local table itself. MRR knows which buffered row a range
answers, but a range carries only its key.

Solution: the server buffers the earlier tables' rows as BKA does and
hands the engine one condition, pushdown, in which their values are
parameters; then, call by call, it hands over the tuples of parameter
values of the buffered rows. The engine prepares pushdown once and
evaluates it for each tuple next to the data. Each row it returns names
its tuple by offset, and the server maps the offset back to the buffered
rows and rechecks them. JOIN_CACHE_MULTI_KEY, derived from
JOIN_CACHE_BKA, takes BKA's place after the join order is fixed, on ref
or eq_ref access to a table whose engine sets HA_CAN_MULTI_KEY_READ,
when the new optimizer_switch flag join_cache_multi_key_read, off by
default, is on. EXPLAIN adds "Multi-key read" after "Using join buffer
(flat, BKA join)".

The interface is three handler methods in the style of index condition
pushdown; MRR is left untouched:

    index_multi_key_read_with_pushdown_prepare(index, param, params,
                                              pushdown)
                                            once per optimization
    for each execution of the join:
      ha_index_init(index)
      for each call a refill of the join buffer splits into:
        index_multi_key_read_params(row_data)
        index_multi_key_read(record, &offset)  until HA_ERR_END_OF_FILE
      ha_index_end()

Design:

- pushdown is a deep copy of the conjuncts of the table's condition (for
  an inner table of an outer join, its ON expression) that use the
  remote table, and otherwise only the buffered and the const tables. It
  keeps the ref's key equality, one top-level = or <=> on each key part
  the ref uses, so the engine finds the key to look up and the residual
  to filter by in one condition.
- Parameters are copies of the buffered tables' fields in a
  Virtual_tmp_table, param[] is its field array, and pushdown's
  Item_fields point at the copies. A const table's field stays a
  constant: a parameter is whatever is in param[].
- A tuple is a record of that table: the parameter values only, in fixed
  record format, so that tuples compare byte for byte. params() gets all
  tuples of a call at once, as a nullptr-terminated array in a buffer
  the server owns; the engine reads tuple i by moving param[] by
  row_data[i] - row_data[0].
- The server sorts the tuples by key, merges exact duplicates and sends
  tuples that share a key but differ in parameters in successive calls.
  So each returned row answers exactly one tuple, and the engine never
  deduplicates. Outer joins NULL-complement the unmatched buffered rows
  as under BKA.

The server guarantees:

- no NULL key value in a tuple;
- no two tuples in one call with keys the index finds equal;
- param[] at row_data[0] when params() starts;
- a recheck of every returned row, except the key equalities test_if_ref
  leaves out and an index condition the engine accepted through
  idx_cond_push.

The engine must:

- answer prepare with 0 or HA_ERR_UNSUPPORTED, or raise its own error.
  prepare runs during optimization, EXPLAIN included, and an acceptance
  lasts across executions until the next prepare or reset(). It promises
  no call, so pushdown applies only to the rows read() returns;
- not fail ha_index_init on an accepted index: what can fail belongs in
  params();
- read the values before params() returns and leave param[] at
  row_data[0];
- return for each tuple every row that satisfies pushdown, once. The key
  equalities must hold exactly, since the recheck may leave them out and
  the split into calls relies on them; the rest of pushdown may be
  applied loosely, except an index condition accepted through
  idx_cond_push;
- end a call with HA_ERR_END_OF_FILE, as any other error fails the
  statement, and accept ha_index_end in the middle of a call.

Otherwise the join runs as plain BKA, with the same rows, decided once
per plan: when a key part's value is not the ref's own (it comes from
the outer query, sits only inside an OR, appears twice, or equality
propagation changed it); when a nullable key value meets a part that is
not null-rejecting; when pushdown holds an item with no deep copy, such
as a subquery or a stored function; when a parameter is a BLOB, a TEXT,
or a BIT kept among the null bits; when there is no parameter, or none
comes from an engine other than the remote table's (by partition_ht(), a
derived or temporary table counting as local), since there BKA's one MRR
request beats a call per tuple; and when prepare returns
HA_ERR_UNSUPPORTED.

Limitations:

- The engine must support MRR.
- A partitioned remote table is not supported: ha_partition masks the
  flag.

Tests: this commit only re-records the results that list the
optimizer_switch flags; with the flag off, the join-cache tests
(main.join_cache, the *_jcl6 tests, main.mrr_icp_extra,
main.innodb_mrr_cpk) pass unchanged. DBUG_EXECUTE_IF points simulate out
of memory where the cache and the parameter copies are created. The next
commit adds the test engine TEST_MULTI_KEY.
Georg Richter
test: replace memmem with local ma_memmem helper for portability

Remove reliance on GNU memmem() in tests by using an internal helper,
ensuring consistent test execution across Windows (MSVC) and POSIX.
Sergei Golubchik
remove dead code
Kristian Nielsen
MDEV-41867: Tablespace pre-allocation blocks SHOW BINARY LOGS

The pre-allocation thread was holding purge_binlog_mutex while creating the
next binlog tablespace file. The creation of the file can take a long time,
and a _very_ long time if disk runs full and the function needs to wait for
space to be freed.

Holding the mutex for long blocks other commands from running such as SHOW
BINARY LOGS, and even PURGE BINARY LOGS (which might otherwise be used to
free up space needed).

Holding the mutex across the file creation is unnecessary, the creation only
happens from the single pre-allocation thread, and it doesn't reference any
data that needs to be protected by that mutex. So just delay taking the
mutex until after the file creation.

Make the innodb_binlog_size_in_pages variable into an atomic. It is not
protected by the purge_binlog_mutex (or any other mutex) anyway, and it is
just the "current value" that can be changed by the user at any time. So
using atomic is mostly to document the unsynchronised access.

Signed-off-by: Kristian Nielsen <[email protected]>
Georg Richter
CONC-868: Fix out-of-bounds read in my_uca1400_collation_id_uca400_compat()

my_uca1400_collation_id_uca400_compat() derives a tailoring id from a
UCA-14.0.0 collation id and uses it to index a table of UCA-4.0.0
compatible collation ids. For tailoring ids 26..31 the lookup went
past the end of that table and read adjacent memory.

Check the tailoring id against the table size before indexing, and
return 0 (no compatible id) for ids outside the table.
  • cc-alma8-aarch64: build linux-connector_c failed -  stdio
  • cc-alma9-aarch64: build linux-connector_c failed -  stdio
  • cc-alma9-amd64: build linux-connector_c failed -  stdio
  • cc-bookworm-aarch64: build linux-connector_c failed -  stdio
  • cc-bookworm-amd64: build linux-connector_c failed -  stdio
  • cc-bullseye-aarch64: build linux-connector_c failed -  stdio
  • cc-bullseye-amd64: build linux-connector_c failed -  stdio
  • cc-buster-aarch64: build linux-connector_c failed -  stdio
  • cc-buster-amd64: build linux-connector_c failed -  stdio
  • cc-fedora37-amd64: build linux-connector_c failed -  stdio
  • cc-fedora38-amd64: build linux-connector_c failed -  stdio
  • cc-focal-aarch64: build linux-connector_c failed -  stdio
  • cc-focal-amd64: build linux-connector_c failed -  stdio
  • cc-jammy-aarch64: build linux-connector_c failed -  stdio
  • cc-jammy-amd64: build linux-connector_c failed -  stdio
  • cc-rhel8-aarch64: build linux-connector_c failed -  stdio
  • cc-rhel8-amd64: build linux-connector_c failed -  stdio
  • cc-rhel9-aarch64: build linux-connector_c failed -  stdio
  • cc-rhel9-amd64: build linux-connector_c failed -  stdio
  • cc-rocky8-aarch64: build linux-connector_c failed -  stdio
  • cc-sles15-amd64: build linux-connector_c failed -  stdio
  • win-connector_c_3: 'dojob cd win32 && del CMakeCache.txt && cmake --version && cmake ..\src -G "Visual Studio 17 2022" -A "Win32" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SSL=SCHANNEL -DWITH_MSI=ON -DCURL_INCLUDE_DIR=c:\tools\curl\include && cmake --build . --clean-first --config RelWithDebInfo --target package' failed -  stdio
Georg Richter
CONC-863: Check that name offsets of dynamic columns do not decrease

read_name() computed the length of a column name as the difference of
two name offsets read from the packed column, without checking that the
next offset is not smaller than the current one. The subtraction wrapped
around, and mariadb_dyncol_list_named() passed the result as size to
memcpy().
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Alessandro Vetere
MDEV-39092 BACKUP SERVER: read the Aria index header under its locks

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

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

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

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

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
MDEV-39092 test: Aria writes during a streamed BACKUP SERVER

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

Assisted-by: Claude:claude-opus-5-5
Georg Richter
CONC-869: Fix OOB read in dyncol value readers due to unvalidated interval lengths.

hdr_interval_length() computed a dynamic column value's length as
(next_entry_offset - hdr->offset) without validating either offset
against the real size of the blob's data segment (hdr->data_size).
type_and_offset_read_num()/_named() only reject an offset that exceeds
what its encoding can represent, which has nothing to do with the
actual data available.

Reachable via mariadb_dyncol_unpack() and mariadb_dyncol_json(),
causing OOB reads in dynamic_column_uint_read(),
dynamic_column_double_read(), and ma_dynstr_append_quoted().

Fix hdr_interval_length() to validate both the current and next
entry's offset against hdr->data_size, matching the server's
mysys/ma_dyncol.c. Also fixes dynamic_column_get_internal(), which
returned stale ER_DYNCOL_OK instead of ER_DYNCOL_FORMAT when
find_column() detected the same kind of corrupted interval.

Added tests covering both value readers and the quoting path via
mariadb_dyncol_unpack()/mariadb_dyncol_json(), plus a direct test
for the mariadb_dyncol_get_num()/find_column() path.
  • cc-alma8-aarch64: build linux-connector_c failed -  stdio
  • cc-alma9-aarch64: build linux-connector_c failed -  stdio
  • cc-alma9-amd64: build linux-connector_c failed -  stdio
  • cc-bookworm-aarch64: build linux-connector_c failed -  stdio
  • cc-bookworm-amd64: build linux-connector_c failed -  stdio
  • cc-bullseye-aarch64: build linux-connector_c failed -  stdio
  • cc-bullseye-amd64: build linux-connector_c failed -  stdio
  • cc-buster-aarch64: build linux-connector_c failed -  stdio
  • cc-buster-amd64: build linux-connector_c failed -  stdio
  • cc-fedora37-amd64: build linux-connector_c failed -  stdio
  • cc-fedora38-amd64: build linux-connector_c failed -  stdio
  • cc-focal-aarch64: build linux-connector_c failed -  stdio
  • cc-focal-amd64: build linux-connector_c failed -  stdio
  • cc-jammy-aarch64: build linux-connector_c failed -  stdio
  • cc-jammy-amd64: build linux-connector_c failed -  stdio
  • cc-rhel8-aarch64: build linux-connector_c failed -  stdio
  • cc-rhel8-amd64: build linux-connector_c failed -  stdio
  • cc-rhel9-aarch64: build linux-connector_c failed -  stdio
  • cc-rhel9-amd64: build linux-connector_c failed -  stdio
  • cc-rocky8-aarch64: build linux-connector_c failed -  stdio
  • cc-sles15-amd64: build linux-connector_c failed -  stdio
  • win-connector_c_3: 'dojob cd win32 && del CMakeCache.txt && cmake --version && cmake ..\src -G "Visual Studio 17 2022" -A "Win32" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SSL=SCHANNEL -DWITH_MSI=ON -DCURL_INCLUDE_DIR=c:\tools\curl\include && cmake --build . --clean-first --config RelWithDebInfo --target package' failed -  stdio
Georg Richter
CONC-i864: Fix memory leak in mariadb_dyncol_list_named()

The result array is allocated before the column names are read. When a
name cannot be read, the function returned ER_DYNCOL_FORMAT without
freeing the array and without resetting *names, so the memory was
leaked and the caller was left with a non-NULL pointer although the
function reported an error.
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Alessandro Vetere
MDEV-39092 test: Aria log rotation before the end of the backup log

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

Assisted-by: Claude:claude-opus-5-5
Georg Richter
CONC-i860: Add STMT_ATTR_CB_RESULT_EX, a result callback with an end pointer

STMT_ATTR_CB_RESULT passes only the current position in the row to the
callback, so a callback cannot check that the data it parses is inside
the row. Add STMT_ATTR_CB_RESULT_EX with the signature

  int callback(void *data, unsigned int column, unsigned char **row,
              const unsigned char *end);

row points to the column's data (the length prefix for variable length
types) and is NULL for NULL values. end is the first byte after the
column's data. The callback must advance *row exactly to end. A non-zero
return value, or a *row different from end, aborts the fetch with
CR_MALFORMED_PACKET.

The legacy and the new callback share storage in MYSQL_STMT (union),
selected by a private type tag, so setting one replaces the other.
Passing NULL clears the callback and returns to the bind_result path.

mysql_stmt_attr_get() supports STMT_ATTR_CB_RESULT, STMT_ATTR_CB_RESULT_EX
and STMT_ATTR_CB_PARAM. The getter for the inactive result callback
returns NULL. Because older libraries reject unknown attributes,
mysql_stmt_attr_get(stmt, STMT_ATTR_CB_RESULT_EX, &cb) can be used to
detect support without side effects.
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Nikita Malyavin
MDEV-41238 mysqltest: add PS_bind_long command

mysqltest gets a PS_bind_long command. It binds like PS_bind, but
sends every value with mysql_stmt_send_long_data() before each
PS_execute, so a test can reach a parameter in LONG_DATA_VALUE state.
Oleksandr Byelkin
MDEV-41738 MEMORY BTREE: DELETE by key prefix skips rows

Regression from MDEV-40186: for unique BTREE keys heap_rkey/rnext/rprev
updated lastkey_len but not lastkey, so a scan restarted after DELETE
searched from a stale key.

Always save the found key in lastkey for BTREE indexes.

Assisted-By: Claude Opus 5.5 <[email protected]>
Sergei Golubchik
update C/C
Nikita Malyavin
MDEV-39927 Multi-key read: test engine TEST_MULTI_KEY

No engine in the server tree implements the multi-key read, so no test
in the tree takes the path. TEST_MULTI_KEY is a MEMORY engine that
does: for each tuple, its read looks up the tuple's key and returns the
rows the pushed condition admits. Session variables count its prepares,
calls, tuples and rows.

Its suite test_multi_key runs each query with join_cache_multi_key_read
off, through SET STATEMENT, and then on, and shows the counters and the
EXPLAIN of the path. The case with a ? takes the path because of
MDEV-41238, which gives a bound ? a copy.

ha_heap is no longer final and is exported from mariadbd, as ha_maria
is, so that the dynamic plugin can derive from it.
Georg Richter
Merge branch '3.3-serg' into 3.3
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Nikita Malyavin
MDEV-41238 follow-up: widen the assertion in Item_param::shallow_copy()

On main, Query_arena::free_items() makes a debug-only shallow copy of
each item class it frees (MDEV-41631). When reprepare frees the old
items of a prepared statement, the statement arena is the THD's own
conventional one, so the assertion against copying an Item_param on the
statement arena fired: main.ps_ddl crashed in debug builds. This is the
way a conventional arena reaches an Item_param, which the review of
PR #5906 left open.

Solution: the assertion that review suggested, which lets a conventional
arena pass; a copy on a prepared statement's arena still fails it.
Georg Richter
Bug fix for MYSQL: Use DATETIME intead of TIME
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Alessandro Vetere
MDEV-39092 BACKUP SERVER: end the Aria log after the table copy

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

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

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

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

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

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

Assisted-by: Claude:claude-opus-5-5
Nikita Malyavin
MDEV-41238 Bound parameters prevent condition pushdown into a grouped view, while CAST enables it

Item_param::deep_copy() returned NULL, and every rewrite that copies a
condition gives up on what it cannot copy. A conjunct holding a
prepared statement parameter was therefore left out of:
- pushdown into derived tables and views, including UNION branches
  and conditions on a derived table column whose expression holds a
  parameter;
- pushdown from HAVING to WHERE, for the part extracted from an OR;
- pushdown into a materialized IN subquery;
- split materialization, which then found no split key;
- the UPPER(col) IN (...) rewrite, which has skipped such conjuncts
  since MDEV-40006.
When the derived table groups by an expression with a parameter, the
condition was pushed into its HAVING instead of its WHERE.

CAST(? AS ...) worked around this only by accident. Before the
pushdown, constant propagation can replace a constant expression on
one side of = with a literal. A bare parameter already counts as a
basic constant, so propagation leaves it as it is. A client whose
charset differs from the column's got the pushdown by a similar
accident: the charset conversion replaces the parameter with a
literal.

Item_param::shallow_copy() now copies a parameter that holds a value,
NULL included, and deep_copy() calls it, as in other leaf items. Every
such copy is made while optimizing during EXECUTE, and the next
EXECUTE makes a new one, so a copy always carries the value bound for
the EXECUTE that made it. A copy that outlived its EXECUTE would keep
an old value and point into a string buffer that the next binding may
free, so shallow_copy() asserts that it does not allocate on the
statement's arena.

The copy is not added to m_clones. m_clones keeps the parameter's
copies in CTE definitions updated with each new value for the whole
life of the statement, while this copy lives for one execution.

An unbound parameter, DEFAULT and IGNORE still have no copy.


On main the new derived_cond_pushdown cases record key_length 260 and
rows 20 where 11.4 records 68 and 10, since the default character set
is utf8mb4, and the CTE case estimates rows 1 for the ref into
<derived3> where 11.4 estimates 10.
Alessandro Vetere
Aria: ma_commit() flushes the log up to an uninitialized LSN

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

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

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

Assisted-by: Claude:claude-opus-5-5
Alessandro Vetere
Aria: double free after a failed log file creation

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

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

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

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

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

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

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

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

Assisted-by: Claude:claude-opus-5-5
Nikita Malyavin
MDEV-39927 Multi-key read: test engine TEST_MULTI_KEY

No engine in the server tree implements the multi-key read, so no test
in the tree takes the path. TEST_MULTI_KEY is a MEMORY engine that
does: for each tuple, its read looks up the tuple's key and returns the
rows the pushed condition admits. Session variables count its prepares,
calls, tuples and rows.

Its suite test_multi_key runs each query with join_cache_multi_key_read
off, through SET STATEMENT, and then on, and shows the counters and the
EXPLAIN of the path. The case with a ? takes the path because of
MDEV-41238, which gives a bound ? a copy.

ha_heap is no longer final and is exported from mariadbd, as ha_maria
is, so that the dynamic plugin can derive from it.
Georg Richter
Add placeholder in emum_stmt_attr_type

Since value 207 (STMT_ATTR_SQL_STATEMENT) is already used in 3.4
we have to add a placeholder (STMT_ATTR_UNUSED).
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Rucha Deodhar
MDEV-41812: Columns named old/new of other tables are resolved as the
OLD/NEW row inside trigger bodies, giving ER_TRG_NO_SUCH_ROW_IN_TRG and
ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION

Analysis:
Inside the trigger's BEGIN...END block, impossible fields are not checked
and the mechanism directly falls on checking whether it is new or old
reference.

Fix:
Check whether the fields are impossible and only then resolve the context
of new and old.
Nikita Malyavin
MDEV-39927 Multi-key read: batch index condition pushdown for non-parametrizeable series of ranges

A join that reads a local table first and looks up a remote table by key
pays for the remote table in round trips or in rows. Remote means that
the engine keeps its data on other machines, so every request is a
network round trip, and that it can evaluate a condition next to the
data. Ref access sends one request per local row. BKA sends one MRR
request for all buffered keys, but the engine returns every row that
shares a key, and the server filters them by the local values
afterwards. In

    SELECT w.sym, t.id, t.ts, t.price
      FROM watch w STRAIGHT_JOIN trades t
        ON t.sym = w.sym AND t.ts >= w.since AND t.price > w.min_price

with 4 local watch rows, 200 trades rows share a sym with them and 10
satisfy the ON clause: BKA returns 200 rows, where an engine that
evaluated the condition would return 10.

No pushdown interface allows this, because none gives the engine a
condition over the local values together with the values of every
buffered row. cond_push sees only the table's own columns. Under BKA the
server keeps an index condition over other tables to itself and
evaluates it on each fetched tuple (cache_idx_cond, skip_index_tuple).
Pushdown of a select or a derived table to the handlerton would make the
engine read the local table itself. MRR knows which buffered row a range
answers, but a range carries only its key.

Solution: the server buffers the earlier tables' rows as BKA does and
hands the engine one condition, pushdown, in which their values are
parameters; then, call by call, it hands over the tuples of parameter
values of the buffered rows. The engine prepares pushdown once and
evaluates it for each tuple next to the data. Each row it returns names
its tuple by offset, and the server maps the offset back to the buffered
rows and rechecks them. JOIN_CACHE_MULTI_KEY, derived from
JOIN_CACHE_BKA, takes BKA's place after the join order is fixed, on ref
or eq_ref access to a table whose engine sets HA_CAN_MULTI_KEY_READ,
when the new optimizer_switch flag join_cache_multi_key_read, off by
default, is on. EXPLAIN adds "Multi-key read" after "Using join buffer
(flat, BKA join)".

The interface is three handler methods in the style of index condition
pushdown; MRR is left untouched:

    index_multi_key_read_with_pushdown_prepare(index, param, params,
                                              pushdown)
                                            once per optimization
    for each execution of the join:
      ha_index_init(index)
      for each call a refill of the join buffer splits into:
        index_multi_key_read_params(row_data)
        index_multi_key_read(record, &offset)  until HA_ERR_END_OF_FILE
      ha_index_end()

Design:

- pushdown is a deep copy of the conjuncts of the table's condition (for
  an inner table of an outer join, its ON expression) that use the
  remote table, and otherwise only the buffered and the const tables. It
  keeps the ref's key equality, one top-level = or <=> on each key part
  the ref uses, so the engine finds the key to look up and the residual
  to filter by in one condition.
- Parameters are copies of the buffered tables' fields in a
  Virtual_tmp_table, param[] is its field array, and pushdown's
  Item_fields point at the copies. A const table's field stays a
  constant: a parameter is whatever is in param[].
- A tuple is a record of that table: the parameter values only, in fixed
  record format, so that tuples compare byte for byte. params() gets all
  tuples of a call at once, as a nullptr-terminated array in a buffer
  the server owns; the engine reads tuple i by moving param[] by
  row_data[i] - row_data[0].
- The server sorts the tuples by key, merges exact duplicates and sends
  tuples that share a key but differ in parameters in successive calls.
  So each returned row answers exactly one tuple, and the engine never
  deduplicates. Outer joins NULL-complement the unmatched buffered rows
  as under BKA.

The server guarantees:

- no NULL key value in a tuple;
- no two tuples in one call with keys the index finds equal;
- param[] at row_data[0] when params() starts;
- a recheck of every returned row, except the key equalities test_if_ref
  leaves out and an index condition the engine accepted through
  idx_cond_push.

The engine must:

- answer prepare with 0 or HA_ERR_UNSUPPORTED, or raise its own error.
  prepare runs during optimization, EXPLAIN included, and an acceptance
  lasts across executions until the next prepare or reset(). It promises
  no call, so pushdown applies only to the rows read() returns;
- not fail ha_index_init on an accepted index: what can fail belongs in
  params();
- read the values before params() returns and leave param[] at
  row_data[0];
- return for each tuple every row that satisfies pushdown, once. The key
  equalities must hold exactly, since the recheck may leave them out and
  the split into calls relies on them; the rest of pushdown may be
  applied loosely, except an index condition accepted through
  idx_cond_push;
- end a call with HA_ERR_END_OF_FILE, as any other error fails the
  statement, and accept ha_index_end in the middle of a call.

Otherwise the join runs as plain BKA, with the same rows, decided once
per plan: when a key part's value is not the ref's own (it comes from
the outer query, sits only inside an OR, appears twice, or equality
propagation changed it); when a nullable key value meets a part that is
not null-rejecting; when pushdown holds an item with no deep copy, such
as a subquery or a stored function; when a parameter is a BLOB, a TEXT,
or a BIT kept among the null bits; when there is no parameter, or none
comes from an engine other than the remote table's (by partition_ht(), a
derived or temporary table counting as local), since there BKA's one MRR
request beats a call per tuple; and when prepare returns
HA_ERR_UNSUPPORTED.

Limitations:

- The engine must support MRR.
- A partitioned remote table is not supported: ha_partition masks the
  flag.

Tests: this commit only re-records the results that list the
optimizer_switch flags; with the flag off, the join-cache tests
(main.join_cache, the *_jcl6 tests, main.mrr_icp_extra,
main.innodb_mrr_cpk) pass unchanged. DBUG_EXECUTE_IF points simulate out
of memory where the cache and the parameter copies are created. The next
commit adds the test engine TEST_MULTI_KEY.
Georg Richter
Infer fixes (dead code)
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
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
  • cc-x-codbc-windows: 'dojob pwd if '3.4' == '3.4' ls win32/test SET TEST_DSN=master SET TEST_DRIVER=master SET TEST_PORT=3306 SET TEST_SCHEMA=odbcmaster if '3.4' == '3.4' cd win32/test if '3.4' == '3.4' ctest --output-on-failure' failed -  stdio
Sergei Golubchik
update C/C
Sergei Golubchik
compilation error

libmariadb\unittest\libmariadb\internal.c(295,21): error C2124: divide or mod by zero
double inf= 1.0 / 0.0; /* or use INFINITY from <math.h> if preferred */
                ^
Rucha Deodhar
MDEV-41812: Columns named old/new of other tables are resolved as the
OLD/NEW row inside trigger bodies, giving ER_TRG_NO_SUCH_ROW_IN_TRG and
ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION

Analysis:
Current implementation did not check whether fields are impossible
(no select involved). This made columns named "old" or "new" falsely
recognized as trigger row references.

Fix
Fixed the condition to check fields_are_impossible() so that column names
can be correctly recognized.