Console View
|
Categories: connectors experimental galera main |
|
| connectors | experimental | galera | main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
bsrikanth-mariadb
srikanth.bondalapati@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36096: Assertion failure in recompute_join_cost_with_limit An assert was present in method recompute_join_cost_with_limit() to make sure the recomputed cost is always >= 0. Although, the assert was correct, it was failing in CLang compiled versions due to floating point comparison, when we set sql_select_limit=1, and optimizer_join_limit_pref_ratio=1; In GCC compiled versions, the partial_join_cost was computed to +0.0. However, in CLang version, the cost turned out to be -0.0. Changed the assert such that partial_join_cost, would now be checked for a value >= -DBL_EPSILON. Following it, we set the partial_join_cost to 0, if it has a negative value. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41152: Fix FILE_CREATE recovery fil_name_process(): Treat FILE_CREATE in the same way as FILE_MODIFY that led to a FIL_LOAD_DEFER return. Remove the parameter lsn, and return file_name_t& in which the caller may assign create_lsn when processing a FILE_CREATE record. deferred_spaces.reinit_all(): Never create anything for deleted tablespaces. Doing so could cause a legitimate file to be deleted if files are being deleted and re-created with the same name. deferred_spaces::item::lsn: Remove. Starting with commit 37d8577aee3bd87b5b04464144d064063b169039 (MDEV-40728) each FILE_ record is parsed only once. recv_sys_t::parse_store_if_exists(): Tell the caller to skip tablespaces for which both FILE_CREATE and FILE_DELETE was parsed. This improves performance, not correctness. recv_validate_tablespace(): Avoid duplicated tablespace lookup and remove a redundant deferred_spaces.add(); fil_name_process already keeps deferred_spaces in sync with recv_spaces. (cherry picked from commit 12b41e06ade1571d81757abf47cc2f1cd8e8d98f) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
bsrikanth-mariadb
srikanth.bondalapati@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39868 Wrong result with a window fn over merged derived table column Problem: ======== A query with a window function over a column of a merged derived table returns an empty set when another table is joined on a condition over the same column and is accessed with "Range checked for each record": SELECT AVG(subq.c2) OVER (), t2.c1 FROM t1 STRAIGHT_JOIN (SELECT * FROM t3) AS subq ON t1.c1 = subq.c1 STRAIGHT_JOIN t2 ON subq.c2 > t2.c1; With derived_merge=on all the references to subq.c2 are Item_direct_view_ref objects sharing one underlying Item_field, because their ref pointers all point into the derived table's field_translation. Item::split_sum_func2() calls real_item() and puts that shared Item_field into the list of the window function's temporary table fields, so create_tmp_field_from_item_field() sets its result_field to a column of the temporary table. Item_field::val_int() reads field, but Item_field::save_in_field() reads result_field, so the two now return different values. The join condition is evaluated through the same Item_field, and the runtime range analysis in Field::get_mm_leaf_int() uses save_in_field_no_warnings(). It reads the still empty temporary table column instead of the value of t3.c2, treats the value as NULL, and builds a SEL_TREE::IMPOSSIBLE. Table t2 then produces no rows. Solution: ========= Do not unwrap Item_direct_view_ref in Item::split_sum_func2(). The wrapper is created per reference and is not shared, so the temporary table field is attached to the wrapper alone and the conditions that refer to the same view column keep reading the base table field. Item_ref::create_tmp_field_ex() already creates the same temporary table field for a view ref over a column, and change_to_use_tmp_fields() already handles REF_ITEM, so no other change is needed. Ref access was never affected: get_store_key() takes real_item()->field explicitly. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41152: Fix FILE_CREATE recovery fil_name_process(): Treat FILE_CREATE in the same way as FILE_MODIFY that led to a FIL_LOAD_DEFER return. Remove the parameter lsn, and return file_name_t& in which the caller may assign create_lsn when processing a FILE_CREATE record. deferred_spaces.reinit_all(): Never create anything for deleted tablespaces. Doing so could cause a legitimate file to be deleted if files are being deleted and re-created with the same name. deferred_space.create(): Remove some duplicated code. Missing tablespace files will be created in fil_node_open_file_low() starting with commit 759e3523e3d832b174cf0a612704da38b2557b40 (MDEV-38026). deferred_spaces::item::lsn: Remove. Starting with commit 37d8577aee3bd87b5b04464144d064063b169039 (MDEV-40728) each FILE_ record is parsed only once. recv_sys_t::parse_store_if_exists(): Tell the caller to skip tablespaces for which both FILE_CREATE and FILE_DELETE was parsed. This improves performance, not correctness. recv_validate_tablespace(): Avoid duplicated tablespace lookup and remove a redundant deferred_spaces.add(); fil_name_process already keeps deferred_spaces in sync with recv_spaces. fil_space_t::rename(): If !log, assert !replace and that the target file name does not exist. os_file_rename_func(): Do not check that the target path does not exist. This is already checked by every caller. This fixes a debug assertion failure that could otherwise occur when recovering from a crash in fil_space_t::rename() between the write of the FILE_RENAME and the actual rename. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Clean up comment in binlog_base64_flag.test Remove unnecessary comment from test file Co-authored-by: Copilot Autofix powered by AI <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Mohammad Tafzeel Shams
tafzeel.shams@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-35154 : dict_sys_t::load_table() is holding exclusive dict_sys.latch for unnecessarily long time Issue: dict_load_table_one() was invoked with the exclusive dict_sys.latch held and never released it. The whole load ran inside that one critical section: reading the SYS_TABLES record, opening the .ibd file, and reading SYS_COLUMNS, SYS_VIRTUAL, SYS_INDEXES, SYS_FIELDS, the clustered index root page, and SYS_FOREIGN, as well as loading every table related by FOREIGN KEY constraints. Almost all of that is I/O. Because dict_sys.latch is the single latch that guards every dictionary lookup, one cold table open blocked every other session from opening any table, including tables that were already cached. A slow enough load could trigger the fatal semaphore wait check. Fix: Split the load into three phases. A short latched phase creates the table object and publishes it as an incomplete "stub"; the I/O runs with no latch held; a final latched phase links the FOREIGN KEY constraints. The stub's progress is held in the top two bits of dict_table_t:: n_ref_count (LOADING_DEF, LOADING_FK, LOAD_FAILED), analogous to how buf_page_t::state() combines a small lifecycle state with the buffer-fix count in one atomic. A thread that finds a loading table waits by pinning it (dict_table_t::try_pin_for_wait(), so it cannot be freed while unlatched) and acquiring load_latch in shared mode. A failed load can be torn down safely even though other threads may have already taken such a pin to wait on it: try_pin_for_wait() refuses once LOAD_FAILED is set, and the teardown path drains any pins taken earlier before freeing the stub. A table remains hidden (LOADING_FK) until every table related to it by FOREIGN KEY constraints has been loaded as well, and dict_sys_t:: load_table() makes them all visible in one step. The intermediate state is visible to constraint linking via dict_sys_t::find_table_fk(), so that two threads loading tables that reference each other can still link the constraint between them. Tables that InnoDB internal SQL can refer to are loaded without ever releasing the latch. The internal SQL parser is not reentrant and is serialized only by the exclusive dict_sys.latch, and it opens tables in the middle of parsing; releasing the latch there let another thread run the parser concurrently and corrupt its state. Changes: - dict_table_t : hold the progress of loading in the top bits of n_ref_count (std::atomic<uint32_t>): LOADING_DEF, LOADING_FK, LOAD_FAILED, loading(). Add load_latch (separate from lock_latch) with load_latch_init()/destroy()/x_lock()/x_unlock()/s_lock()/ s_unlock(), and the loader-only helpers start_loading(), advance_to_loading_fk(), finish_loading(), mark_load_failed(), plus try_pin_for_wait() for waiters. acquire()/release() are unconditional. Add debug load_thread and is_loader(). - dict_load_table_one() : publish the table as a LOADING_DEF stub in table_non_LRU and release the latch before loading the tablespace, the columns and the indexes; reacquire it, move the table to table_LRU and advance it to LOADING_FK before loading the foreign key constraints. Failures go through dict_load_table_one_discard(). Add the hold_latch parameter and the dict_load_table_one_no_latch debug sync point. - dict_sys_t::load_table() : wait for a concurrent load of the same table via wait_for_load(); allocate the foreign key table names on a local heap, because the latch is released while draining them; in the drain loop, wait for a table whose definition is still being loaded by another thread and skip one that is only waiting for its own related tables; make all tables loaded by this invocation visible in one step, releasing each one's load_latch. - dict_sys_t::wait_for_load() : pins the observed table via try_pin_for_wait() (returning immediately if the load already failed), releases the exclusive dict_sys.latch, blocks on the table's own load_latch, unpins, and reacquires the latch. - dict_load_table_one_discard() : used wherever a stub must be torn down (retry after DB_SUCCESS_LOCKED_REC, column or virtual-column load failure, a corrupted index or missing FK index). Marks the stub LOAD_FAILED, releases load_latch to wake any already-pinned waiters, drains the reference count to zero, then removes the stub. - dict_sys_t::add() : initialise load_latch for every table, and take it in exclusive mode on the loader's behalf before a loading stub becomes reachable via find_table_any(). - dict_sys_t::remove() : also destroy load_latch. - dict_load_hold_latch() : whether a table may be referenced by InnoDB internal SQL, and therefore must be loaded without releasing the latch: InnoDB system tables, FULLTEXT INDEX auxiliary tables and the persistent statistics tables. - dict_load_table_on_id() : copy the table name and release the SYS_TABLES page latch before calling load_table(), which may now block. Restore the cursor position only if the scan has to continue. - dict_load_foreign(), dict_load_foreigns() : add the fk_heap parameter and allocate the names appended to fk_tables on it. - dict_sys_t::find_table_any() : the previous body of find_table(), returning tables that are being loaded. Only the name is read. - dict_sys_t::find_table() : hide tables that are being loaded, both in the by-name and the by-id variant. In the by-id variant, loading is checked before any bit-field, to avoid a torn read. - dict_sys_t::find_table_fk() : like find_table(), but LOADING_FK tables are returned, so that constraints can be linked into them while holding the exclusive latch. - dict_table_can_be_evicted() : a table that is being loaded may only be freed by the thread that is loading it. - dict_foreign_add_to_cache() : resolve both sides with find_table_fk(). - btr_search_disable() : skip tables that are being loaded. Walking their indexes would race with the loading thread, which appends to that list without holding the latch. Such tables cannot have any adaptive hash index references. - create_table_info_t::create_foreign_keys() : take a temporary reference on a referenced table as soon as it is resolved, released on every exit path by a scope guard; call dict_sys.prevent_eviction() only once the constraint is actually committed to the dictionary cache, replacing the temporary reference. - create_table_info_t::create_table() : acquire a reference to the created table around the foreign key handling, and use a local heap for the names of the foreign key related tables. - row_rename_table_for_mysql() : use one local heap for both the dropped-constraint names and the foreign key table names. - assertion fix : the load path may now run without dict_sys.latch, but only in the thread that is loading the table, and a cached table is no longer necessarily fully loaded. dict_sys.locked() is relaxed to "dict_sys.locked() || table->is_loader()" in dict_load_columns(), dict_load_virtual_col(), dict_load_fields(), dict_load_indexes(), dict_index_add_to_cache(), dict_index_find_cols(), dict_index_build_internal_clust(), dict_index_build_internal_non_clust() and dict_index_build_internal_fts(). Checks of dict_table_t::cached are relaxed and reordered after the atomic loading in dict_table_add_system_columns(), dict_sys_t::add(), dict_table_rename_in_cache() and hash_insert(). - innodb.dict_load_concurrent A load is parked at dict_load_table_one_no_latch while holding no latch; another table can be loaded meanwhile, and a second opener of the same table waits. A second case checks that a table is not made visible while a table related to it by a FOREIGN KEY constraint is still being loaded by another thread. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41166 --backup --innodb-log-checkpoint-now may copy too much xtrabackup_backup_func(): Request for a checkpoint synchronously so that recv_sys.find_checkpoint() will observe the effect. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alessandro Vetere
iminelink@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41201 LRUItr::start(): fix inverted reset condition The scan hand pointer m_hp must stay in place while it is still inside the old (cold) sublist of the LRU list, and rewind to the tail only once it has advanced past the old/young boundary into the young (hot) sublist. The condition was inverted, so start() rewound the pointer to the tail on every call instead of only at that boundary, defeating the intended scan-resumption optimization and biasing scans toward the tail of the old sublist. Port of percona/percona-server@dc344fba7e3d272ac18e8d6589b37c678f1e1ad5 by Paweł Olchawa (PS-11446). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Yuchen Pei
ycp@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39525 [to-squash] fix longstr supertype check The string supertype check is too strict: it bails when src (resp. dst) is length-limited in chars (i.e. CHAR/VARCHAR) and dst (resp. src) is length-limited in octets (i.e. TEXT/BLOB). We change this to conversion of src length limit to that of dst, i.e. - if src limit is declared in octets and dst limit in chars, convert src limit to be in chars, and compare - if src limit is declared in chars and dst limit in octets, convert src limit to be in octets, and compare This restores the JSON_EXTRACT cases in vcol.vcol_sargable |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
bsrikanth-mariadb
srikanth.bondalapati@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39868 Wrong result with a window fn over merged derived table column Problem: ======== A query with a window function over a column of a merged derived table returns an empty set when another table is joined on a condition over the same column and is accessed with "Range checked for each record": SELECT AVG(subq.c2) OVER (), t2.c1 FROM t1 STRAIGHT_JOIN (SELECT * FROM t3) AS subq ON t1.c1 = subq.c1 STRAIGHT_JOIN t2 ON subq.c2 > t2.c1; With derived_merge=on all the references to subq.c2 are Item_direct_view_ref objects sharing one underlying Item_field, because their ref pointers all point into the derived table's field_translation. Item::split_sum_func2() calls real_item() and puts that shared Item_field into the list of the window function's temporary table fields, so create_tmp_field_from_item_field() sets its result_field to a column of the temporary table. Item_field::val_int() reads field, but Item_field::save_in_field() reads result_field, so the two now return different values. The join condition is evaluated through the same Item_field, and the runtime range analysis in Field::get_mm_leaf_int() uses save_in_field_no_warnings(). It reads the still empty temporary table column instead of the value of t3.c2, treats the value as NULL, and builds a SEL_TREE::IMPOSSIBLE. Table t2 then produces no rows. Solution: ========= Do not unwrap Item_direct_view_ref in Item::split_sum_func2(). The wrapper is created per reference and is not shared, so the temporary table field is attached to the wrapper alone and the conditions that refer to the same view column keep reading the base table field. Item_ref::create_tmp_field_ex() already creates the same temporary table field for a view ref over a column, and change_to_use_tmp_fields() already handles REF_ITEM, so no other change is needed. Ref access was never affected: get_store_key() takes real_item()->field explicitly. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
WIP: log tracking BACKUP SERVER TO ... CONCURRENT (for HAVE_INNODB_PMEM) backup_sink::id: The thread identifier (0 to CONCURRENT-1) innodb_backup_checkpoint_pmem(): Copy the old log file. InnoDB_backup::log_track(), InnoDB_backup::log_track_pmem(): Keep copying the log until we run out of InnoDB data files to copy. InnoDB_backup::checkpoint_complete_pmem(): Copy the remaining part of an old log file right before it is being released. InnoDB_backup::commit(): In log tracking backup, copy the rest of the HAVE_INNODB_PMEM log. FIXME: Implement the non-PMEM code path with minimal blocking. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39993 Use CREATE OR REPLACE for sys schema routines mariadb-upgrade silently dropped EXECUTE grants on sys schema stored functions and procedures. The sys schema install scripts reinstalled every routine with DROP FUNCTION/PROCEDURE IF EXISTS followed by CREATE. DROP cascades to delete the routine's rows in mysql.procs_priv, so any EXECUTE grant a DBA had issued on e.g. sys.table_exists or sys.quote_identifier was lost every time mariadb-upgrade reinstalled the sys schema, even though the routine itself came back unchanged. Fix: replace DROP ... IF EXISTS + CREATE with CREATE OR REPLACE in all 53 sys_schema function/procedure files and in the two templates (templates/function.sql, templates/procedure.sql) so future routines follow the same pattern. CREATE OR REPLACE PROCEDURE/FUNCTION goes through sp_drop_routine_internal(), which only deletes the mysql.proc row and never reaches sp_revoke_privileges() (that is only called from the explicit DROP PROCEDURE/FUNCTION statement), so mysql.procs_priv is left untouched and existing grants survive. This mirrors the pattern already used by sys schema views (CREATE OR REPLACE ... VIEW, since MDEV-9077), which never had this problem. Four of the converted files (functions/format_path.sql, functions/ps_is_account_enabled_57.sql, procedures/ps_setup_reset_to_default.sql, procedures/ps_trace_thread_57.sql) are not referenced by scripts/sys_schema/CMakeLists.txt; they were converted anyway for consistency and have no behavioural effect. As a side effect, a pre-existing UDF whose name collides with a sys routine name is no longer destroyed before the reinstall fails: DROP FUNCTION IF EXISTS resolved the UDF namespace first, silently dropping the UDF and then failing on ER_SP_ALREADY_EXISTS anyway; CREATE OR REPLACE fails immediately on ER_UDF_EXISTS with the UDF intact. scripts/maria_add_gis_sp.sql.in and the sys_config triggers were deliberately left untouched: the GIS procedures are only re-installed at bootstrap time (mariadb-upgrade instead patches their definer in place via UPDATE), and triggers carry no procs_priv rows, so neither is on the code path this bug is about. Added mysql-test/main/mysql_upgrade_sys_routine_grants.test, which grants EXECUTE on sys.table_exists and sys.quote_identifier, overwrites both routine bodies with a marker to prove the upgrade actually reinstalls them (rather than the sys schema install being skipped), runs mariadb-upgrade, and checks both that the grants survived and that the real routine bodies came back. Co-Authored-By: Claude Sonnet 5 <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed. 1. Row-based events work for already locked tables. slave_close_thread_tables() unconditionally called trans_commit_stmt()/trans_rollback_stmt(), both of which cannot be done under sub-statment. In test case a BINLOG statement with malformed base64 payload executed from an AFTER INSERT trigger hit this: mysql_client_binlog_statement() sets thd->is_error() on decode failure and calls slave_close_thread_tables(), which then asserted since the trigger body runs as a sub-statement. The fix guards the commit/rollback with !thd->in_sub_stmt, matching the same idiom already used in mysql_execute_command() and open_and_lock_tables() for the same reason: sub-statements defer statement-transaction finalization to the enclosing top-level statement. Other callers of slave_close_thread_tables() run only from the top-level SQL slave applier thread, so this doesn't change their behavior. rows_event_stmt_cleanup() has its own trans_commit_stmt()/ trans_rollback_stmt() call after applying a row event; guard it the same way. Rows_log_event::do_apply_event() opens its target tables via a one-shot "if (!thd->lock)" check that only fires at the top of a fresh statement. Inside a trigger, thd->lock already belongs to the enclosing DML, so the table is never opened, leaving a NULL TABLE* used further down. Look it up among the enclosing statement's already open tables (find_locked_table()) instead, and raise ER_TABLE_NOT_LOCKED if not found -- the same error a normal trigger-body statement gets for referencing an unprelocked table. 2. Statement-based events are disabled. A BINLOG statement decoding to a Query_log_event, executed from within a trigger or stored routine, cannot be made to work easily. DML for statement event in trigger cannot be done for new tables because the locking must be done at once and it cannot be done for query tables because of ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG. - Query_log_event::do_apply_event() runs the embedded query via mysql_parse(), which assumes it starts a genuinely new top-level statement (THD::reset_for_next_command() asserts !spcont/!in_sub_stmt and documents itself as "not called by substatements of routines"; lex_start() reinitializes thd->main_lex, which the general BINLOG-statement code reuses as scratch space on the assumption that it is idle -- false while the enclosing statement is still running). This part is fixable: parse into a private LEX and a private Query_arena instead of going through mysql_parse(), the same way mysql_make_view() parses embedded SQL text mid-statement. - What isn't easily fixable is resolving the tables the embedded query references. Prelocking is computed statically from the trigger body's own SQL text; a table name decoded at runtime from a BINLOG payload can never be part of it. open_table(), in prelocked mode, only treats an already-open table as reusable when its query_id is 0 (free) -- a table still owned by the not-yet-finished enclosing statement is refused (ER_NO_SUCH_TABLE), and lock_tables() applies the same kind of check for a table the trigger writes back into (ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG). Bypassing that check (reusing the same TABLE object instead of a second, prelocked instance) removes the very guard that stops a trigger recursing into itself: a trigger on t1 whose BINLOG payload inserts into t1 recursed without limit, surfacing not as controlled recursion but as save_restore_context_apply_event()'s "!rli->mi" assert, since that scratch slot on the shared, fake rli is not reentrant. Refuse the combination outright instead: Query_log_event::do_apply_event() now raises ER_SP_BADSTATEMENT when thd->in_sub_stmt, before touching any THD state. 3. Query_log_event PS execute leak fix A BINLOG statement decoding to a Query_log_event, executed via PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's own persistent arena: thd->stmt_arena pointed at it while mysql_parse() ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY, since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a successful execution. The fix redirects thd->stmt_arena to thd itself for the duration of the nested mysql_parse() call, so stmt_arena->is_conventional() reads true and activate_stmt_arena_if_needed() (called e.g. from save_leaf_tables()) never redirects allocations to the PS's arena in the first place. Harmless for what it's protecting: leaf_tables_exec is normally cached on the persistent arena so a repeatedly-executed statement's SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn down and reparsed fresh (due to mysql_parse() semantics) on every EXECUTE, so there's nothing to cache here regardless of which arena is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Yuchen Pei
ycp@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39525 [to-squash] Check the constant is strictly inside the field domain The "both bounded" check in vcol_type_both_bounded() used Field::is_supertype() for the constant as well. That is imprecise in both directions: - It compares declared data types, and Item_int always reports INT or BIGINT, so for a TINYINT/SMALLINT/MEDIUMINT vcol the check never passes, not even for `tinyint_vcol_expr = 2`. - Being representable in the field's data type is not enough. Field::store() maps out-of-domain values onto the extreme values of the domain: numbers are clamped, strings are truncated. So a comparison against a constant survives the conversion only if no other value can be mapped onto the constant, that is, only if the constant lies strictly inside the domain. For `a tinyint, va tinyint as (a+1)` and a row with a=127, the expression is 128 while va is clamped to 127, so both `a+1 > 127` and `a+1 = 127` give different results before and after the substitution. Add Field::is_const_strictly_inside_domain(), which stores the constant into the field and checks that - the value was stored exactly (as the range optimizer does in Field::get_mm_leaf_int() and friends), and - the stored value is not an extreme value of the domain. The second part is a per-family virtual: integer fields compare against Type_limits_int, string fields compare the length of the stored value against the capacity limit, in the unit in which the data type declares it (characters for CHAR/VARCHAR, octets for BLOB/TEXT). CHAR fields under a NO PAD collation are excluded altogether, as the store pads the value with spaces. Data types without an override keep returning false, which only means a missed optimization. Also expand eligible Item_bool_rowready_func2 to include "<", "<=", ">" and ">=". This restores explain results with int in vcol.vcol_sargable cases. Note, Field::is_supertype() is still needed for the vcol field against the vcol expression: it covers the case when the conversion is not lossy at all, including a constant sitting exactly on the boundary of the domain. Co-Authored-By: Claude Opus 5 <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thirunarayanan Balathandayuthapani
thiru@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MDEV-28730 fixup: clang -Wunused-but-set-global | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fix ps2 protocol consequences | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Rucha Deodhar
rucha.deodhar@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40490: SET NEW = (row subquery) in a trigger silently assigns NULL to all columns and skips the single-row check Analysis: Assigning a multi-column subquery in a row trigger was resulting in NULL values because the Item_cache objects wrapped around the columns weren't being evaluated yet. Fix: Calling bring_value() on the RHS item forces the subquery to run and populate those caches properly before we try to assign them, fixing the silent data loss. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alexander Barkov
bar@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39563 Implement UPDATE ... RETURNING ... INTO Adding support for UPDATE .. RETURNING .. INTO queries. For example: UPDATE t1 SET a=10,b=20 RETURNING a,b INTO va,vb; UPDATE t1 SET a=10,b=20 RETURNING a,b INTO @a,@b; Limitations: 1. These types of queries: - REPLACE .. RETURNING .. INTO - DELETE .. RETURNING .. INTO - INSERT .. RETURNING .. INTO do not work - they return an error. They will be implemented separately, when needed. 2. UPDATE..RETURNING..INTO with --binlog_format=statement is not allowed and an error is raised. 3. Using OLD_VALUE(col) inside UPDATE..RETURNING..INTO is not allowed and an error is raised. 4. Multi-table updates, as well as single table updates with a subquery to the same table in WHERE (which get converted to multi-table) do not work and an error is raised. Notes: 1. ANALYZE and EXPLAIN Both ANALYZE UPDATE .. RETURNING .. INTO .. EXPLAIN UPDATE .. RETURNING .. INTO .. return this error: 'RETURNING..INTO' is not allowed in this context 2. Behavior on no data a. In case of degenerated plans (WHERE 1=0, LIMIT 0), no errors are raised. b. If the updated table contains no rows, the behavior depends on the engine, for example: - MyISAM returns no errors - InnoDB raises No data - zero rows fetched, selected, or processed This behavior is engine dependent because some engines (e.g. MyISAM) quickly know that the table has no records and execute the statement using a degenerated plan. c. If there are some rows, but non of them match the WHERE condition, then this error is raised: No data - zero rows fetched, selected, or processed d. If some rows where found but none of them actually got changed by the SET, still this error is raised: No data - zero rows fetched, selected, or processed The error message might be misleading. However, if we read it as "zero rows [that required updates] fetched", it looks OK. Let's not introduce a new error message for now. Helper changes: 1. The grammar in analyze_stmt_command was changed to have LEX::analyze_stmt set to true earlier, so LEX::set_returning_into_result() already knows if this is an ANALYZE statement. 2. The Sql_cmd_update constructor is now called earlier in the grammar, to be able to call Sql_cmd_update::set_with_old_value_items() in the SET and RETURNING clauses. 3. Sql_cmd_dml::lex is now set during the constructor time. It makes things easier: - Sql_cmd_update::returns_result_set() needs the lex. - Sql_cmd_delete::orig_multitable and Sql_cmd_update::orig_multitable are not needed any more. They were used only in Sql_cmd_delete::sql_command_code() and Sql_cmd_update::sql_command_code(). Sql_cmd_dml::sql_command_code() now returns lex->sql_command. The overrides Sql_cmd_delete::sql_command_code() and Sql_cmd_update::sql_command_code() were removed. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
bsrikanth-mariadb
srikanth.bondalapati@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-36356 Server crash in Item::save_int_in_field with Window functions A tail (ORDER BY / LIMIT / locking clause) that follows a parenthesized query expression is parsed while the select inside the parentheses is the current one, so everything the tail registers - its window functions, the window specifications they introduce and the units of its subqueries - ends up registered in that inner select. When the parenthesized query expression already has a tail of its own, LEX::add_tail_to_query_expression_body_ext_parens() wraps it into a derived table and attaches the new tail to the wrapping select. The registrations were left behind in the inner select, so the ORDER BY of the wrapping select contained window function items that no select had registered: they never got a Window_funcs_sort, were never computed, and their result field was read unset. The subqueries of the tail kept the inner select as their master and name resolution context. Move these registrations to the wrapping select in the new move_tail_registrations(): - The window functions of the tail are the ones its ORDER BY items contain, looked up with Item::walk() and walk_subquery == FALSE so that a window function belonging to a subquery of the tail is not found and stays registered where it was parsed. Each one takes along the window specification it introduced; "OVER win_name" has none of its own, it is looked up by name at fix_fields() time and never reaches window_specs. - Item::walk() does not descend into a window specification, so the items of a moved specification's PARTITION BY and ORDER BY lists are not re-targeted by Lex_order_limit_lock::set_to() and are pointed at the wrapping select's name resolution context here. - The subquery units of the tail are the ones registered in front of the first unit the inner select had when the tail started to be parsed; the grammar remembers it in a mid-rule action. There is no equivalent of the walk above for them: a unit records neither the clause it came from nor its position in the parsed text. They are excluded from the inner select, re-registered in the wrapping select and their Item_subselect::parent_select is updated. Per-select parse state that is not a registration (n_sum_items, with_sum_func, with_rownum, ftfunc_list, uncacheable, and the select_n_where_fields of the tail's own fields) is deliberately left behind, where it is merely over-counted. Also remove the unreachable !unit check - unit is dereferenced on entry - and fix the indentation of the surrounding block. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Yuchen Pei
ycp@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39525 [to-squash] Move collation match check outside of longstr is_supertype This allows WHERE json_unquote(json_extract(...))=<const_string> to use compare_collation instead. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fix embedded server | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fix tests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alessandro Vetere
iminelink@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41201 LRUItr::start(): fix inverted reset condition The scan hand pointer m_hp must stay in place while it is still inside the old (cold) sublist of the LRU list, and rewind to the tail only once it has advanced past the old/young boundary into the young (hot) sublist. The condition was inverted, so start() rewound the pointer to the tail on every call instead of only at that boundary, defeating the intended scan-resumption optimization and biasing scans toward the tail of the old sublist. Port of Percona Server commit dc344fba7e3d272ac18e8d6589b37c678f1e1ad5 by Paweł Olchawa (PS-11446). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed. slave_close_thread_tables() unconditionally called trans_commit_stmt()/trans_rollback_stmt(), which assert !thd->in_sub_stmt. A BINLOG statement with malformed base64 payload executed from an AFTER INSERT trigger hits this: on decode failure, mysql_client_binlog_statement() sets thd->is_error() and calls slave_close_thread_tables() from within the trigger's sub-statement. Guard it with spcont/in_sub_stmt, deferring cleanup to the enclosing top-level statement. Other callers run only from the top-level SQL slave applier thread, so this doesn't change their behavior. 1. BINLOG statement executed from a trigger, SF or SP is disabled by the patch: Row events (Rows_log_event) open their target table via a one-shot check that only fires at the top of a fresh statement. Inside a trigger, the table is never opened this way. It may be fixed by reusing query_tables, but: - find_locked_table() matched only by table name -- could return the TABLE instance the enclosing statement was actively writing through, not an idle one. Reusing it would require pre-saving its state: record[0]/bitmaps/handler/etc. (will deprecate ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG) - set_stmt_row_injection()/set_time() calls mutated thd->lex, which at that point is main_lex -- shared with the enclosing statement, not something safe to touch. Statement events (Query_log_event) run the embedded query via mysql_parse(): it bundles lex_start(), reset_for_next_command() and parse_sql() as one unit meant for a genuinely new top-level statement, not a one-off nested parse. Calling parse_sql() directly instead avoids that, but then we own everything mysql_parse() was doing for us: a private LEX and a private Query_arena (or allocations land on main_lex/whatever arena is currently active, shared with the enclosing statement), plus calling mysql_execute_command() ourselves afterwards. In any case, DML for query_tables cannot be done due to ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG reasons explained above. 2. PS for BINLOG statement still works and needs a leak fix for statement events: A BINLOG statement decoding to a Query_log_event, executed via PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's own persistent arena: thd->stmt_arena pointed at it while mysql_parse() ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY, since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a successful execution. The fix redirects thd->stmt_arena to thd itself for the duration of the nested mysql_parse() call, so stmt_arena->is_conventional() reads true and activate_stmt_arena_if_needed() (called e.g. from save_leaf_tables()) never redirects allocations to the PS's arena in the first place. Harmless for what it's protecting: leaf_tables_exec is normally cached on the persistent arena so a repeatedly-executed statement's SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn down and reparsed fresh (due to mysql_parse() semantics) on every EXECUTE, so there's nothing to cache here regardless of which arena is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41166 --backup --innodb-log-checkpoint-now may copy too much xtrabackup_backup_func(): Request for a checkpoint synchronously so that recv_sys.find_checkpoint() will observe the effect. (cherry picked from commit b5ab07c314698e5fa15e5010fb7b78b1728306b5) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
test2 possible fix for fedora 44 uninitialized warning Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Try to fix install | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed. slave_close_thread_tables() unconditionally called trans_commit_stmt()/trans_rollback_stmt(), which assert !thd->in_sub_stmt. A BINLOG statement with malformed base64 payload executed from an AFTER INSERT trigger hits this: on decode failure, mysql_client_binlog_statement() sets thd->is_error() and calls slave_close_thread_tables() from within the trigger's sub-statement. Guard it with spcont/in_sub_stmt, deferring cleanup to the enclosing top-level statement. Other callers run only from the top-level SQL slave applier thread, so this doesn't change their behavior. 1. BINLOG statement executed from a trigger, SF or SP is disabled by the patch: Row events (Rows_log_event) open their target table via a one-shot check that only fires at the top of a fresh statement. Inside a trigger, the table is never opened this way. It may be fixed by reusing query_tables, but: - find_locked_table() matched only by table name -- could return the TABLE instance the enclosing statement was actively writing through, not an idle one. Reusing it would require pre-saving its state: record[0]/bitmaps/handler/etc. (will deprecate ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG) - set_stmt_row_injection()/set_time() calls mutated thd->lex, which at that point is main_lex -- shared with the enclosing statement, not something safe to touch. Statement events (Query_log_event) run the embedded query via mysql_parse(): it bundles lex_start(), reset_for_next_command() and parse_sql() as one unit meant for a genuinely new top-level statement, not a one-off nested parse. Calling parse_sql() directly instead avoids that, but then we own everything mysql_parse() was doing for us: a private LEX and a private Query_arena (or allocations land on main_lex/whatever arena is currently active, shared with the enclosing statement), plus calling mysql_execute_command() ourselves afterwards. In any case, DML for query_tables cannot be done due to ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG reasons explained above. 2. PS for BINLOG statement still works and needs a leak fix for statement events: A BINLOG statement decoding to a Query_log_event, executed via PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's own persistent arena: thd->stmt_arena pointed at it while mysql_parse() ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY, since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a successful execution. The fix redirects thd->stmt_arena to thd itself for the duration of the nested mysql_parse() call, so stmt_arena->is_conventional() reads true and activate_stmt_arena_if_needed() (called e.g. from save_leaf_tables()) never redirects allocations to the PS's arena in the first place. Harmless for what it's protecting: leaf_tables_exec is normally cached on the persistent arena so a repeatedly-executed statement's SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn down and reparsed fresh (due to mysql_parse() semantics) on every EXECUTE, so there's nothing to cache here regardless of which arena is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
misc fixes 1. set PLUGIN_HEX_VERSION correctly for bundled plugins 2. don't add GenError dependency for external plugins 3. don't change the policy globally 4. only do EXTERNAL_PLUGIN_POST() if EXTERNAL_PLUGIN_PRE() was done |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleksandr Byelkin
sanja@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fix windows test | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed. slave_close_thread_tables() unconditionally called trans_commit_stmt()/trans_rollback_stmt(), which assert !thd->in_sub_stmt. A BINLOG statement with malformed base64 payload executed from an AFTER INSERT trigger hits this: on decode failure, mysql_client_binlog_statement() sets thd->is_error() and calls slave_close_thread_tables() from within the trigger's sub-statement. Guard it with spcont/in_sub_stmt, deferring cleanup to the enclosing top-level statement. Other callers run only from the top-level SQL slave applier thread, so this doesn't change their behavior. 1. BINLOG statement executed from a trigger, SF or SP is disabled by the patch: Row events (Rows_log_event) open their target table via a one-shot check that only fires at the top of a fresh statement. Inside a trigger, the table is never opened this way. It may be fixed by reusing query_tables, but: - find_locked_table() matched only by table name -- could return the TABLE instance the enclosing statement was actively writing through, not an idle one. Reusing it would require pre-saving its state: record[0]/bitmaps/handler/etc. (will deprecate ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG) - set_stmt_row_injection()/set_time() calls mutated thd->lex, which at that point is main_lex -- shared with the enclosing statement, not something safe to touch. Statement events (Query_log_event) run the embedded query via mysql_parse(): it bundles lex_start(), reset_for_next_command() and parse_sql() as one unit meant for a genuinely new top-level statement, not a one-off nested parse. Calling parse_sql() directly instead avoids that, but then we own everything mysql_parse() was doing for us: a private LEX and a private Query_arena (or allocations land on main_lex/whatever arena is currently active, shared with the enclosing statement), plus calling mysql_execute_command() ourselves afterwards. In any case, DML for query_tables cannot be done due to ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG reasons explained above. 2. PS for BINLOG statement still works and needs a leak fix for statement events: A BINLOG statement decoding to a Query_log_event, executed via PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's own persistent arena: thd->stmt_arena pointed at it while mysql_parse() ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY, since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a successful execution. The fix redirects thd->stmt_arena to thd itself for the duration of the nested mysql_parse() call, so stmt_arena->is_conventional() reads true and activate_stmt_arena_if_needed() (called e.g. from save_leaf_tables()) never redirects allocations to the PS's arena in the first place. Harmless for what it's protecting: leaf_tables_exec is normally cached on the persistent arena so a repeatedly-executed statement's SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn down and reparsed fresh (due to mysql_parse() semantics) on every EXECUTE, so there's nothing to cache here regardless of which arena is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sergei Golubchik
serg@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
misc fixes 1. set PLUGIN_HEX_VERSION correctly for bundled plugins 2. don't add GenError dependency for external plugins 3. don't change the policy globally |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Yuchen Pei
ycp@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39525 [to-squash] fix longstr supertype check The string supertype check is too strict: it bails when src (resp. dst) is length-limited in chars (i.e. CHAR/VARCHAR) and dst (resp. src) is length-limited in octets (i.e. TEXT/BLOB). We change this to conversion of src length limit to that of dst, i.e. - if src limit is declared in octets and dst limit in chars, convert src limit to be in chars, and compare - if src limit is declared in chars and dst limit in octets, convert src limit to be in octets, and compare This restores the JSON_EXTRACT cases in vcol.vcol_sargable |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed. slave_close_thread_tables() unconditionally called trans_commit_stmt()/trans_rollback_stmt(), which assert !thd->in_sub_stmt. A BINLOG statement with malformed base64 payload executed from an AFTER INSERT trigger hits this: on decode failure, mysql_client_binlog_statement() sets thd->is_error() and calls slave_close_thread_tables() from within the trigger's sub-statement. Guard it with spcont/in_sub_stmt, deferring cleanup to the enclosing top-level statement. Other callers run only from the top-level SQL slave applier thread, so this doesn't change their behavior. 1. BINLOG statement executed from a trigger, SF or SP is disabled by the patch: Row events (Rows_log_event) open their target table via a one-shot check that only fires at the top of a fresh statement. Inside a trigger, the table is never opened this way. It may be fixed by reusing query_tables, but: - find_locked_table() matched only by table name -- could return the TABLE instance the enclosing statement was actively writing through, not an idle one. Reusing it would require pre-saving its state: record[0]/bitmaps/handler/etc. (will deprecate ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG) - set_stmt_row_injection()/set_time() calls mutated thd->lex, which at that point is main_lex -- shared with the enclosing statement, not something safe to touch. Statement events (Query_log_event) run the embedded query via mysql_parse(): it bundles lex_start(), reset_for_next_command() and parse_sql() as one unit meant for a genuinely new top-level statement, not a one-off nested parse. Calling parse_sql() directly instead avoids that, but then we own everything mysql_parse() was doing for us: a private LEX and a private Query_arena (or allocations land on main_lex/whatever arena is currently active, shared with the enclosing statement), plus calling mysql_execute_command() ourselves afterwards. In any case, DML for query_tables cannot be done due to ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG reasons explained above. 2. PS for BINLOG statement still works and needs a leak fix for statement events: A BINLOG statement decoding to a Query_log_event, executed via PREPARE/EXECUTE, leaked its nested query's allocations onto the PS's own persistent arena: thd->stmt_arena pointed at it while mysql_parse() ran the decoded query. Second EXECUTE asserted on ROOT_FLAG_READ_ONLY, since PROTECT_STATEMENT_MEMROOT marks that arena read-only after a successful execution. The fix redirects thd->stmt_arena to thd itself for the duration of the nested mysql_parse() call, so stmt_arena->is_conventional() reads true and activate_stmt_arena_if_needed() (called e.g. from save_leaf_tables()) never redirects allocations to the PS's arena in the first place. Harmless for what it's protecting: leaf_tables_exec is normally cached on the persistent arena so a repeatedly-executed statement's SELECT_LEX doesn't rebuild it every time, but our SELECT_LEX is torn down and reparsed fresh (due to mysql_parse() semantics) on every EXECUTE, so there's nothing to cache here regardless of which arena is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fixup! 204b63badbe6ef38b3a57a4bd0c429b76ac48c63 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Kristian Nielsen
knielsen@knielsen-hq.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Test possible fix for fedora 44 uninitialized warning Signed-off-by: Kristian Nielsen <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||