Console View
|
Categories: connectors experimental galera main |
|
| connectors | experimental | galera | main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Extract vers_set_starts() and add starts_clause Extracts the STARTS-alignment logic into vers_set_starts() so it can be reused. If the aligned local time falls into a DST gap (e.g. a midnight that does not exist), the start of the gap is used instead of failing with ER_PART_WRONG_VALUE. If it falls before the TIMESTAMP range (e.g. a local midnight before 1970), the timestamp itself is used. Also adds the Vers_part_info::starts_clause flag in preparation for later commits. The flag records whether STARTS was given explicitly (by the user or in the unpacked frm), which the auto-partitioning code uses to decide whether to re-align STARTS to the existing history. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41508 Off-by-One Uninitialized Stack Read in Proxy Protocol V2 Port Parsing parse_v2_header() required only 25 (TCPv4) / 49 (TCPv6) bytes, but then reads the source port at hdr[25] / hdr[49]. A truncated header could make it read uninitialized stack. Require the whole address block: 28 bytes for TCPv4 and 52 for TCPv6. Also, the declared header length was trusted without checking how many bytes vio_read() returned. Reject short reads. The spec requires the sender to send the header at once, and allows the receiver to drop the connection on a partial header, so a single vio_read() is enough. The v1 parser had the same problem on EOF: vio_read() returning 0 was taken for a byte read, so the uninitialized buffer was parsed. Require exactly one byte. Extend test_proxy_header_tcp with a header cut short before the port, and add test_proxy_header_short for a header shorter than it declares. Light refactoring, to avoid repetition: added connect_tcp() and init_v2_header(), which are now shared by several proxy header tests. Assisted-by: Claude:claude-sonnet-5-5 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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). REPLACE had the same problem with record[2]. TABLE::delete_row() saves the new row into record[2] and turns the conflicting row into history with vers_update_end(). Its VCOL_UPDATE_FOR_WRITE computation stored the virtual blobs of the old row into Field_blob::value, the buffer record[2] points to, so the new row was written with them. Versioned DELETE had the same problem with record[1]: it points to the virtual blobs computed with VCOL_UPDATE_FOR_DELETE in Field_blob::value, and vers_update_end() overwrote them. 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(). For REPLACE and DELETE, TABLE::delete_row() keeps Field_blob::value with remember_blob_values() while it updates the history row. In the RBR appliers no VCOL_UPDATE_FOR_WRITE followed vers_update_fields(), so record[1] was not overwritten there. Their explicit computation replaces the one removed from vers_update_fields(). It uses VCOL_UPDATE_FOR_WRITE, so an error in a virtual field expression now stops the slave instead of being turned into a warning. In the Update_rows applier, vers_insert_history_row() also overwrote the error of ha_update_row(), so a failed update on the slave was ignored and its history row was written. The history row is now written only when the update succeeded. Based on the MDEV-24646/MDEV-39711 fix by ParadoxV5 (0c2f3de1a2d), which switched vers_update_fields() to VCOL_UPDATE_FOR_WRITE. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rex Johnston
rex.johnston@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40012 PQ: refuse a plan that reads a table through a join buffer A worker has no join buffer. setup_worker_jointabs() stripped the cache off every worker JOIN_TAB and joined a row at a time, and pwt_table_conds() handed the worker the buffer's scan filter (cache_select->cond) as well as select_cond so that it still rejected what the buffer would have. The result was correct, but it was not the plan the optimizer costed: BNL, BNLH, BKA and BKAH were chosen for scanning the inner table once per buffer-full of outer rows, and a worker scanned it once per driving row instead. For a full-scan inner table that can be orders of magnitude more work than the serial plan the parallel one is meant to beat. So the gate now refuses the plan, as the prototype did. The check sits in can_run_query_in_workers() beside the other per-table refusals, on tab->cache or tab->use_join_cache, both of which make_join_readinfo() has settled by the time parallel_join_check() runs in optimize_stage2(). The refusal names the table in the optimizer trace. The worker-side handling of a buffered table goes with it: - pwt_table_conds() becomes pwt_table_cond() and no longer reports cache_select->cond. remove_redundant_bnl_scan_conds() only moves conjuncts out of select_cond when the tab has a BNL/BNLH cache, so for every tab the gate now admits select_cond is already the whole condition. A tab can still carry a cache_select without a buffer, because make_join_select() builds the scan filter before check_join_cache_usage() decides, but its cond is then a copy of conjuncts select_cond still holds. The gate no longer asks whether that copy is worker-safe, since no worker evaluates it. - pwt_clone_table_conds(), which cloned both halves and ANDed them, is gone; setup_worker_jointabs() clones the one condition with pwt_clone_rebind(). - setup_worker_jointabs() no longer clears cache, use_join_cache and jbuf_tracker on the worker's copy; pwt_assert_tab_inert() asserts that the manager's tab has no cache instead. The copy's cache_select is still cleared, as it can be set on a tab without a buffer. parallel_query_join: the BNL case now expects the query to run serially, with the same rows. The full-scan inner table case (d3) was getting a BNL buffer under the default join_cache_level, so it runs with join_cache_level=0 to keep covering a worker-side full scan of an inner table. parallel_query_why pins the new refusal reason. This commit was prepared with Claude Code (Opus 5.5), which wrote the gate check, removed the worker-side join buffer handling it made unreachable, updated the tests, reconfigured and rebuilt the worktree, and ran the parallel_query tests in normal and --ps-protocol modes. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40799 Runtime plugin/UDF load errors lost under --silent-startup Regression from MDEV-32745 (7828fb475b0), which guarded the plugin-load my_error() calls with opt_silent_startup. That option is a lifetime global, set once at startup and never reset, so the guard suppressed the SQL error for the whole server lifetime, not just during startup. Runtime operations (INSTALL PLUGIN, CREATE FUNCTION ... SONAME) then skipped my_error(), never set the diagnostics area and wrongly succeeded - e.g. main.ps's "call proc_1()" no longer failed with ER_CANT_OPEN_LIBRARY. Startup callers pass MYF(ME_ERROR_LOG); runtime callers pass MYF(0). Gate the silencing on that flag via silent_plugin_startup() so it applies only to the startup error-log path, and runtime errors always reach the client. No new test case: the runtime failure path is already covered by existing tests (e.g. main.ps's ER_CANT_OPEN_LIBRARY check). The regression stayed invisible only because stock MTR does not start servers with --silent-startup. A dedicated test would have to restart the server with --silent-startup solely to assert that a startup-only option does not affect runtime, which adds little over the restored invariant. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Replace COMBINE macro with interval2sec() and interval2usec() Turn the COMBINE macro of date_add_interval() into inline functions with overloads for INTERVAL and MYSQL_TIME, so that the history partition sizing can reuse the seconds part. Move the "Daynumber" comment that sat above the macro to MAX_DAY_NUMBER, which it describes. MAX_DAY_NUMBER moves to sql_time.h as a constexpr (the unused copy in item_timefunc.cc is removed), and the range check of date_add_interval() becomes the inline interval_days_out_of_range(), so that the sizing can check that the seconds do not overflow. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41508 Off-by-One Uninitialized Stack Read in Proxy Protocol V2 Port Parsing parse_v2_header() required only 25 (TCPv4) / 49 (TCPv6) bytes, but then reads the source port at hdr[25] / hdr[49]. A truncated header could make it read uninitialized stack. Require the whole address block: 28 bytes for TCPv4 and 52 for TCPv6. Also, the declared header length was trusted without checking how many bytes vio_read() returned. Reject short reads. The spec requires the sender to send the header at once, and allows the receiver to drop the connection on a partial header, so a single vio_read() is enough. The v1 parser had the same problem on EOF: vio_read() returning 0 was taken for a byte read, so the uninitialized buffer was parsed. Require exactly one byte. Extend test_proxy_header_tcp with a header cut short before the port, and add test_proxy_header_short for a header shorter than it declares. Light refactoring, to avoid repetition: added connect_tcp() and init_v2_header(), which are now shared by several proxy header tests. Assisted-by: Claude:claude-sonnet-5-5 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Comment on partition_element allocation in set_up_default_partitions() partition_element and its push_back() list node are allocated on the active arena (thd->mem_root). The allocation failure is already reported there (by my_malloc() or the arena's error handler), which is why the failure path only returns the error. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Fix to_string() OOM return semantics for temporal types Temporal_hybrid::to_string() returned the destination String with a stale length when String::alloc() failed, instead of signalling the failure. Return NULL on allocation failure. Apply the same fix to the sibling to_string() methods of Interval_DDhhmmssff, Time, Datetime and Date, which shared the identical flaw, so all five report OOM consistently. The val_str() implementations that return the to_string() result directly did not set null_value, so a NULL result would be taken for a value. They now go through the new Item::check_str(), which sets null_value from the result, so a NULL does not stick to the item for the next execution. The time_to_string_oom debug keyword makes Time::to_string() fail as on out of memory. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER Adds logic into prep_alter_part_table() for INTERVAL ... AUTO to read the history range (vers_get_history_range()) and size the new layout from it. STARTS is aligned to the oldest history row_end (vers_set_starts()) if it was not specified by the user, or if the user specified it later than that; in the latter case WARN_VERS_WRONG_STARTS is printed. The history partitions cover both the newest history row_end and the query time, so DML after the ALTER does not need to create partitions. A PARTITIONS N clause is kept if it gives more partitions. An explicit subpartition list is replaced by as many default subpartitions. If the layout needs more than MAX_PARTITIONS partitions (counting subpartitions), or the INTERVAL is longer than the date range, the ALTER fails with ER_VERS_HIST_PART_FAILED. Month and year intervals are stepped in UTC; a boundary past the maximum TIMESTAMP ends a partial last history partition. In case of fast ALTER TABLE, f.ex. when partitioning already exists, the above logic is ignored unless FORCE clause is specified. When user specifies partition list explicitly the above logic is ignored even with FORCE clause. With FORCE on a table already partitioned by SYSTEM_TIME the work is done on a clone of the partitioning info. The parsed settings are copied into the clone's vers_info with Vers_part_info::copy_settings(), which keeps the clone's now_part and hist_part (the parsed ones are NULL). Before reading the range, the metadata lock is upgraded to MDL_SHARED_NO_WRITE, and then the copy does not run online, so DML cannot add history that would not fit the computed layout until the ALTER ends (Alter_info::vers_history_read). LOCK=NONE and ALGORITHM=INPLACE/NOCOPY/INSTANT are rejected for such ALTER before the upgrade, so they do not wait for concurrent DML; the ALGORITHM error is reported by the new Alter_info::report_algorithm_unsupported(), which mysql_alter_table() uses as well. The range is read under a table lock taken with mysql_lock_tables(), as some engines (Aria) set up their transaction in THR_LOCK callbacks. The scan can be killed. WARN_VERS_STARTS_BEYOND_INTERVAL for ALTER .. AUTO is checked after the alignment, so it is not printed when STARTS was re-aligned to the history. vers_get_history_range() detects if an index can be used for row_end min/max stats and if so it gets it with ha_index_first() and HA_READ_BEFORE_KEY (as it must ignore current data). Otherwise it does a table scan to read the stats. Current rows with the pre-11.5 row_end marker are not taken for history either. In the index they sort below history after 2038, so the minimum is read after them. There is test_mdev-25529 debug keyword to check the both and compare results. WARN_VERS_SLOW_ROW_END is printed if the scan read any row and there is no usable index (a disabled one does not count as missing). Static key_cmp was renamed to key_eq to resolve compilation after key.h was included as key_cmp was already declared there. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 ErrConvTimestamp for printing timestamps Add ErrConvTimestamp, an ErrConv that formats a Timestamp in the session time zone into a fixed buffer for warning and error messages, with the number of fractional digits given explicitly. ErrBuff::set_mysql_time() gets an optional digits argument for it. Use it when printing STARTS and the history range. Rename the error symbol ER_PART_STARTS_BEYOND_INTERVAL to WARN_VERS_STARTS_BEYOND_INTERVAL and extend its message: it now takes the STARTS timestamp and the query timestamp as arguments, so the number of format arguments changes from one to three. The error number (4164) is unchanged, so this is not an ABI break, but libmariadb still exports the old symbol name for that number until its submodule is updated. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Nikita Malyavin
nikitamalyavin@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 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. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40287 Use MD5_CTX directly for MD5, like the SHA wrappers my_md5() faked an EVP_MD_CTX in a stack buffer sized by EVP_MD_CTX_SIZE, the same opaque-context hack MyCTX used for the cipher - and its only guard, check_openssl_compatibility(), was removed earlier in this patch. MD5_CTX is a concrete struct in both OpenSSL and WolfSSL (WolfSSL provides the OpenSSL-compatible MD5 API), so use it directly with MD5_Init/Update/Final on a caller-owned context, exactly like my_sha.inl does for SHA*_CTX. This removes the EVP_MD_CTX hack and EVP_MD_CTX_SIZE; my_md5_context_size() now returns sizeof(MD5_CTX). SHA is unaffected - it already used concrete SHA*_CTX structs. Assisted-by: Claude:claude-opus-4-8 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rex Johnston
rex.johnston@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40012 PQ: refuse DEFAULT() in an expression a worker evaluates DEFAULT(col) was evaluated against the worker's row. Item_default_value is an Item_field whose field is a private copy made by make_default_field(), reading the share's default_values but carrying the column's own table. Its deep_copy() shares that field, and Pwt_field_rebinder, seeing the manager's table, repointed it at the worker's real column. DEFAULT(col) then read the current row's value, and for an expression default calculate() called set_default() and wrote the default into the row -- wrong results in a release build, a marked_for_write assertion in a debug one. It shows only when the expression also names the column some other way; alone, DEFAULT(col) is table-independent and evaluated at optimize time. The gate now refuses any expression holding a DEFAULT(), found with check_func_default_processor, which only Item_default_value answers. Found by running the main suite with --parallel-worker-threads=4, where type_temporal_innodb lost a "Truncated incorrect BOOLEAN value" warning because the worker's WHERE evaluated to NULL. parallel_query_clone covers a lost row, a gained row and the expression default that crashed, and checks that none of them ran in the workers. This commit was prepared with Claude Code (Opus 5.5), which traced the wrong result from a main-suite sweep with workers enabled to the rebinder, wrote the gate refusal and the test, confirmed the test fails without the refusal, and ran the parallel_query tests in normal and --ps-protocol modes. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MDEV-39306 Comments on update_virtual_fields() modes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41493 AFTER UPDATE trigger reads wrong NEW value of BLOB/TEXT virtual column in system-versioned table UPDATE, multi-table UPDATE, INSERT ... ON DUPLICATE KEY UPDATE and the RBR Update_rows applier save the new row into record[2] and call vers_insert_history_row(), which restores the old row from record[1] and sets its row_end with vers_update_end(). The VCOL_UPDATE_FOR_WRITE computation in vers_update_end() stored the virtual blobs of the old row into Field_blob::value, the buffer that the virtual blobs of the new row in record[2] point to, or reallocated it when the old value was longer. After restore_record(record[2]), AFTER UPDATE triggers read the old value, or freed memory, as NEW. remember_blob_values() and restore_blob_values() skipped virtual GEOMETRY columns: they selected MYSQL_TYPE_BLOB, while virtual_not_stored_blob_fields counts all fields with BLOB_FLAG. So GEOMETRY had the same problem also in TABLE::delete_row() for REPLACE and DELETE. UPDATE ... FOR PORTION OF has the same problem in period_make_insert(), which computes the inserted leftover rows into record[0] while the new row is saved in record[2], also for tables without system versioning. The fix saves Field_blob::value with remember_blob_values() before the history row in vers_insert_history_row() or the leftover row in period_make_insert() is computed, and puts it back with restore_blob_values() afterwards, as TABLE::delete_row() already does for REPLACE (for DELETE it frees the saved values instead). remember_blob_values() and restore_blob_values() now select fields by BLOB_FLAG. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rex Johnston
rex.johnston@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40012 PQ: a worker must not end its container's scan after handing it over A pre-aggregating worker accumulates into its own container, a temporary table, and hands that container to the manager with sink->flush(). Just before the hand-off, execute_and_handoff() closes the container's index, because the manager starts its own scan of the container immediately after the hand-off. The tidy-up at exec_exit then closed the container a second time. Its comment said this was only for the paths that reach exec_exit without a hand-off, but the code ran on every path, including the path with a successful hand-off. By that point the container may already belong to the manager: claim_next_result() has seen it marked as done, and pwt_tmp_table_source::next_row() has rebound it to the manager's thread and called ha_rnd_init() on it. The worker's ha_index_or_rnd_end() therefore ends the manager's scan. The manager's next ha_rnd_next() then fails "inited == RND" in a debug build, and in a release build it reads from a table with a closed scan. The window between the flush and the second close is only a few instructions wide, so the failure is rare: main.ps_3innodb hit it once in a main-suite run with --parallel-worker-threads=4, on SELECT ... UNION DISTINCT SELECT sum(a) ... GROUP BY b. The second close now runs only on the paths that skipped the first close, as recorded by a flag set next to the first one. parallel_query_handoff makes the race certain. With pwt_handoff_sync set, a worker that has handed over its container waits until the manager reaches the new sync point pwt_manager_scanning_container, and the manager keeps its scan open until the worker has finished its tidy-up. The test uses one worker, so there is a single container and no risk of crossed signals. It checks the rows and that the query ran in the worker. Without the fix it fails the inited == RND assertion on every run. This commit was prepared with Claude Code (Opus 5.5), which found the crash in a main-suite sweep with workers enabled, traced it from the core dump to the second close, wrote the fix, the sync points and the test, and confirmed that the test fails without the fix in five runs out of five and passes with it in normal and --ps-protocol modes. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 ALTER TABLE FORCE syntax improved Lets an alter_list be supplied alongside a partitioning or remove_partitioning clause in either order. This allows the FORCE clause to be added to an existing command, and lets AUTO and FORCE be specified together, which later commits of this task rely on. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Georg Richter
georg@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Just 'delete' the copy, don't cleanup it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
mariadb-PranavTiwari
pranav.tiwari@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MDEV-39343 mysql_upgrade tool now uses mariadb_info table and grant_tables_intact variable to decide whether upgrade is needed or not. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41508 Off-by-One Uninitialized Stack Read in Proxy Protocol V2 Port Parsing parse_v2_header() required only 25 (TCPv4) / 49 (TCPv6) bytes, but then reads the source port at hdr[25] / hdr[49]. A truncated header could make it read uninitialized stack. Require the whole address block: 28 bytes for TCPv4 and 52 for TCPv6. Also, the declared header length was trusted without checking how many bytes vio_read() returned. Reject short reads. The spec requires the sender to send the header at once, and allows the receiver to drop the connection on a partial header, so a single vio_read() is enough. The v1 parser had the same problem on EOF: vio_read() returning 0 was taken for a byte read, so the uninitialized buffer was parsed. Require exactly one byte. Extend test_proxy_header_tcp with a header cut short before the port, and add test_proxy_header_short for a header shorter than it declares. Light refactoring, to avoid repetition: added connect_tcp() and init_v2_header(), which are now shared by several proxy header tests. Assisted-by: Claude:claude-sonnet-5-5 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Petrunia
sergey@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Add comments, Don't remove from THD::free_list. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Khaled Riyad
khaled57.dev@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41140 mariadb-server.postinst chown dereferences symlinks The postinst, preinst, rpm postin and mysqld_safe scripts followed symlinks when fixing ownership and permissions under /var/log/mysql and /var/lib/mysql. Use -h and find -execdir, and let mysqld_safe write as the mysql user, so symlinks there are no longer dereferenced. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Vladislav Vaintroub
vvaintroub@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40287 AES_ENCRYPT() and KDF() return NULL with OpenSSL 4.0 MyCTX used an EVP_CIPHER_CTX in a stack buffer instead of allocating it with EVP_CIPHER_CTX_new(). OpenSSL 4.0 rejects a context that was not created that way for ciphers that use an IV: EVP_CipherInit_ex() fails with "invalid iv length", so AES_ENCRYPT() and KDF() in CBC/CTR/GCM modes return NULL. ECB has no IV and still works, which is why only the non-ECB modes broke. Fix: Allocate the context with EVP_CIPHER_CTX_new()/EVP_CIPHER_CTX_free(). Also remove check_openssl_compatibility() and the EVP_CIPHER_CTX_SIZE and EVP_CIPHER_CTX_init macros. Verified against OpenSSL 4.0.1: the mysys aes-t test fails on the CBC/CTR/GCM cases with the stack buffer and passes with EVP_CIPHER_CTX_new(). No visible performance degradation: the extra allocation costs ~12 ns/call (WolfSSL) and ~30 ns (OpenSSL) on Windows at a 30-byte payload, nothing at 16 KB, and nothing on Linux/glibc; sysbench OLTP over encrypted tables and redo log is unchanged. Tests main.tlsv13 and main.ssl_crl: OpenSSL 4.0.3 reports TLS alerts as "tls alert ..." (was "ssl/tls alert ..."), accept both. Assisted-by: Claude:claude-opus-4-8 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Raghunandan Bhat
raghunandan.bhat96@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Nikita Malyavin
nikitamalyavin@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 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
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41617 Index corruption on UPDATE of table with long unique hash collision and indexed virtual blob handler::check_duplicate_long_entry_key() reads all rows with the same long unique hash. Each read (ha_index_read_map() and every ha_index_next_same() hit) runs TABLE::update_virtual_fields() with VCOL_UPDATE_FOR_READ, which swaps Field_blob::value and read_value of virtual blob columns. The swap was undone only after the first read. Every further read swapped again, so the buffer that record[1] (the old row of UPDATE) points to ended up in value: the next read stored the lookup row into it, or restore_blob_values() freed it. InnoDB then built the old secondary index key from overwritten or freed memory and did not find the record to update. The fix undoes the swap after each read. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Mohammad Tafzeel Shams
tafzeel.shams@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41621 Stored generated BLOB column is wrong on REPLACE into versioned table REPLACE into a system-versioned table turns the conflicting row into history in TABLE::delete_row(): it saves the new row into record[2], restores the conflicting row from record[1] and sets its row_end with vers_update_end(). The VCOL_UPDATE_FOR_WRITE computation in vers_update_end() also recomputes stored generated columns. When Field_blob::store() copies the value into Field_blob::value (e.g. on a character set conversion), it overwrote the buffer that the stored generated blob of the new row in record[2] points to, and the new row was written with the value of the history row. The index on the column holds the same value, so CHECK TABLE does not detect it. remember_blob_values(), which keeps Field_blob::value around vers_update_end() in TABLE::delete_row() and vers_insert_history_row() and around update_generated_fields() in period_make_insert(), covered only virtual columns. The fix adds the with_stored parameter to remember_blob_values(), restore_blob_values() and free_blob_values(), which also covers stored generated blob columns. TABLE::delete_row(), vers_insert_history_row() and period_make_insert() use it and size the storage by TABLE_SHARE::virtual_fields, the number of all generated columns. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
mariadb-PranavTiwari
pranav.tiwari@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39343 Add Grant_tables_intact status variable Add the status variable Grant_tables_intact. It is set on server startup and on every FLUSH PRIVILEGES, and tells whether the grant tables in the mysql schema (db, global_priv, roles_mapping, proxies_priv, tables_priv, columns_priv, procs_priv) have the structure this server expects. This makes it possible to detect that the tables restored from a dump of an older version need mysql_upgrade. A grant table that is missing, or that has a wrong structure, is written to the error log and makes the variable OFF. The expected structures are in the new sql/grant_table_defs.cc. They must be updated together with scripts/mysql_system_tables.sql when a column of one of these tables is changed. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rex Johnston
rex.johnston@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
PQ: Code tidy ups and fixups of tidyups. parallel_query_aggregate.test: fix statistics impersonalize language in can_parallel_scan_jointab_access() remove dead code (trace_parallel_scan_options) add an assert to ensure we do not use Item_sum_avg without two packed fields minor comment fixups remove enum element NESTED_LOOP_DECLINED and use a local bool instead fix silly code in pwt_row_layout::build_aggregates (throwing away a pointer then searching for it again later); 'one' starts as nullptr, because GCC cannot see that whole_table is unchanged between the two blocks and warns -Wmaybe-uninitialized in an ASAN build remove pwt_batch* worker->manager streaming functions tidy up Parallel_scan_partitioner::Scan_ctx::create_chunks to use create_chunks_unsplit The 'one= nullptr' initialisation was added with Claude Code (Opus 5.5), which traced the -Wmaybe-uninitialized warning to this change and folded the fix in here. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41550 MTR doesn't produce core dump or stack trace when a server hangs during startup Problem: ======== If mariadbd does not finish starting up, for example it hangs in a restart done by a test, mtr only killed it, so there was no way to see where it was stuck. Solution: ======== sleep_until_file_created(): when the wait for the pid file times out and the process is still alive, send it SIGABRT and wait for it to finish dumping core (MTR_DUMP_CORE_TIMEOUT seconds, default 120). The server's fatal signal handler then writes the stack trace to the error log, which mtr reports as usual. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| get_next_time() comment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER Adds logic into prep_alter_part_table() for INTERVAL ... AUTO to read the history range (vers_get_history_range()) and size the new layout from it. STARTS is aligned to the oldest history row_end (vers_set_starts()) if it was not specified by the user, or if the user specified it later than that; in the latter case WARN_VERS_WRONG_STARTS is printed. The history partitions cover both the newest history row_end and the query time, so DML after the ALTER does not need to create partitions. A PARTITIONS N clause is kept if it gives more partitions. An explicit subpartition list is replaced by as many default subpartitions. If the layout needs more than MAX_PARTITIONS partitions (counting subpartitions), or the INTERVAL is longer than the date range, the ALTER fails with ER_VERS_HIST_PART_FAILED. Month and year intervals are stepped in UTC; a boundary past the maximum TIMESTAMP ends a partial last history partition. In case of fast ALTER TABLE, f.ex. when partitioning already exists, the above logic is ignored unless FORCE clause is specified. When user specifies partition list explicitly the above logic is ignored even with FORCE clause. With FORCE on a table already partitioned by SYSTEM_TIME the work is done on a clone of the partitioning info. The parsed settings are copied into the clone's vers_info with Vers_part_info::copy_settings(), which keeps the clone's now_part and hist_part (the parsed ones are NULL). Before reading the range, the metadata lock is upgraded to MDL_SHARED_NO_WRITE, and then the copy does not run online, so DML cannot add history that would not fit the computed layout until the ALTER ends (Alter_info::vers_history_read). LOCK=NONE and ALGORITHM=INPLACE/NOCOPY/INSTANT are rejected for such ALTER before the upgrade, so they do not wait for concurrent DML; the ALGORITHM error is reported by the new Alter_info::report_algorithm_unsupported(), which mysql_alter_table() uses as well. The range is read under a table lock taken with mysql_lock_tables(), as some engines (Aria) set up their transaction in THR_LOCK callbacks. The scan can be killed. WARN_VERS_STARTS_BEYOND_INTERVAL for ALTER .. AUTO is checked after the alignment, so it is not printed when STARTS was re-aligned to the history. vers_get_history_range() detects if an index can be used for row_end min/max stats and if so it gets it with ha_index_first() and HA_READ_BEFORE_KEY (as it must ignore current data). Otherwise it does a table scan to read the stats. Current rows with the pre-11.5 row_end marker are not taken for history either. In the index they sort below history after 2038, so the minimum is read after them. There is test_mdev-25529 debug keyword to check the both and compare results. WARN_VERS_SLOW_ROW_END is printed if the scan read any row and there is no usable index (a disabled one does not count as missing). Static key_cmp was renamed to key_eq to resolve compilation after key.h was included as key_cmp was already declared there. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rex Johnston
rex.johnston@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40012 PQ: end a worker's parallel scan before close() frees m_prebuilt A worker's handler was destroyed with its parallel scan still open. exec() and scan_only() jump to their exit labels when parallel_init_worker() fails, and the commonest failure is HA_ERR_END_OF_FILE: no chunk was left for this worker, which happens all the time on small tables. InnoDB has allocated m_pscan_worker by then, so the scan was left for ~ha_innobase() -> parallel_scan_free(), which runs after close() has freed m_prebuilt. parallel_scan_free() says it drops the worker without end() for exactly that reason, but ~Parallel_scan_worker() called end() anyway, and end() writes m_prebuilt->pscan_chunk_clamp: a heap-use-after-free that ASAN reports on every such worker. In a build without ASAN the write lands in freed memory, and it is the likely cause of the intermittent SIGSEGV in free() inside pthread_create() when a later worker reused a cached thread stack whose DTV had been overwritten. Three changes close it. Both workers now end the scan at their exit label instead of after the loop, so every way out ends it while the handler is still open; ending a scan that was never begun is harmless. ha_innobase::close() ends any worker or coordinator scan still open before row_prebuilt_free(), so a path that misses it is ended properly rather than left for the destructor; with the SQL layer put back as it was, this alone keeps ASAN clean. And ~Parallel_scan_worker() no longer calls end(), so parallel_scan_free(), now reached only for a handler destroyed without close(), cannot write through m_prebuilt either. The close() hook and the single call site are taken from a patch by a co-worker. Two debug assertions state the rule the defect broke, so it no longer takes ASAN to see it. ha_innobase::close() asserts that the SQL layer has already ended a worker's scan, and parallel_scan_free() asserts that no worker survives close(). The injection pwt_leave_scan_for_close makes the SQL layer leave the scan of a worker that holds no cursor -- one handed no chunk -- for close() to end, so the backstop is exercised on purpose and the first assertion is told to allow it. pwt_end_worker_scan() holds the injection and ends the scan at both exit labels. parallel_query_no_chunk (debug only) runs a full scan and a range scan of a one-page table with four workers, so three of them get no chunk, twice each, through the normal and the scan-only path, with and without the injection, checking the rows and that the workers ran every query. In a plain debug build, removing the exit-label calls fails the first assertion and removing the close() backstop fails the second. ~Parallel_scan_worker() no longer calling end() is defence for release builds and is not reachable while the backstop is there. This commit was prepared with Claude Code (Opus 5.5), which reproduced the pthread_create() crash under ASAN and traced it, wrote the fix, folded in the co-worker's close() hook and single call site, added the debug assertions, the injection and the test, confirmed in a plain debug build that the test fails with each of the two fixes removed, and ran the parallel_query tests in normal and --ps-protocol modes and the main suite with --parallel-worker-threads=4 under ASAN. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||