Console View
|
Categories: connectors experimental galera main |
|
| connectors | experimental | galera | main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Anway Durge
durgeanway@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-40819: Do not crash on DuckDB TIME values beyond 24 hours MariaDB TIME is a duration up to 838:59:59, but SELECT converted DuckDB TIME through ToString(), which asserts clock-time validity in debug builds. Convert microseconds to MYSQL_TIME directly instead. SQL DML quoted TIME strings, so '25:00:00' failed conversion. Store microseconds through mariadb_time() instead, matching the appender path. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Stream empty InnoDB system tablespace files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Marko Mäkelä
marko.makela@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Push posix_fadvise() deeper, and copy up to 16MiB at a time. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Test cleanup: sorted result for versioning.partition Avoid result fluctuations with different storage engines. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Remove C++ STL machinery from sql/rpl_*info_file.* and CHANGE MASTER parser Replaces std::function / std::optional / std::unordered_map / std::unordered_set / std::string_view and template-heavy code in the master.info / relay-log.info persistence layer with plain MariaDB idioms. - On-disk file format unchanged. - SQL syntrax unchanged - No notable test changes, except those for which new error messages was added and output from SHOW VARIABLES. - master_* CLI options moved from inline globals in rpl_master_info_file.h to Sys_var_* in sys_vars.cc (READ_ONLY, AUTO_SET on use_gtid and heartbeat_period). master_heartbeat_period is Sys_var_double (seconds); master_use_gtid is Sys_var_enum. Global storage sits outside HAVE_REPLICATION so libmariadbd links. - This adds the rpl_master_info variables to SHOW VARIABLES - Value classes are no longer templates. Persistent renamed to Value; Optional_* classes renamed (Uint_value_with_default, Path_value, etc.). has_value flag in the base; is_default() non-virtual. Multi-line method bodies moved to new rpl_info_file.cc and rpl_master_info_file.cc. - VALUE_LIST (Mem_fn array) and VALUE_MAP (std::unordered_map) replaced by per-instance Value *const value_list[] / value_map[] arrays + a static TYPELIB populated at first construction from each Value's variable_name. seen-set dedup is now a uint32 bitmask. - Default handling is not anymore done with using special values. Setting a value to default is now done by calling set_default(), which sets the has_value member to 0 and stores the default value in the variable. When reading a value, one does not have to check if the value is a default value. This makes the interface cleaner and easier to manage. - CHANGE MASTER TO parser no longer builds std::function lambdas: LEX_MASTER_INFO carries mi_used_options / mi_used_default bitmasks plus plain value fields; change_master() applies them via bit-test + direct copy or set_default(). std::optional<T> and trilean are gone from the value class API. - Heartbeat_period_value::load_from uses my_strtod() instead of my_decimal for the on-disk format. from_decimal kept for CHANGE MASTER. - RocksDB #if __cplusplus < 201703L guard at the top of rpl_master_info_file.h dropped; header now compiles in C++11. cstdint dropped; uint32_t / UINT32_MAX in touched code replaced by MariaDB's uint32 / UINT_MAX32. include/my_global.h gains INT_BUFFER_SIZE next to LONGLONG_BUFFER_SIZE. - Pointer-style throughout (T * not T &). Multi-line inline functions used in more than one place moved to .cc files per style. New features: - SHOW VARIABLES now shows the master default values. - If there is an wrong value for a row in a master.info file the corresponding variable name and the value is now written to the error log. Bug fixes uncovered by the refactor: - master_ssl_verify_server_cert had a pre-existing inconsistency with security implications: the documented default and the Sys_var_mybool default are TRUE (validate the master's certificate), but the OLD init_ssl_config() set the per-Master_info field to FALSE on RESET SLAVE ALL. Concretely: after RESET SLAVE ALL followed by a bare CHANGE MASTER TO (no explicit MASTER_SSL_VERIFY_SERVER_CERT), the slave would connect with MASTER_SSL=1 enabled but with certificate validation silently disabled. Fixed by not resetting master_ssl_verify_server_cert in RESET SLAVE ALL; - As some replication setups sets master_ssl_verify_server_cert= 0 while other does not do that, the expected value used in check-testcase.inc "Master_SSL_Verify_Server_Cert No" does not make sense. The value of this variable is now ignored by check-testcase.inc - The old code would fail to read a master info file if there was a new variable longer than 23 characters. This is now extended to 80. - Removed warning on RESET SLAVE that "RESET SLAVE is implicitly changing" the value of 'Using_Gtid' from 'No' to 'Slave_Pos'" as this change did not really happen. This error was given if the master could support GTID but the user had specified directly or indirectly to not use GTID. Other things - Size of mariadb (optimize build) shrunk with 1,408,009 bytes (Based on 'dec' from the size command) - Size of code in sql directory did not change notable. - Reworked Master_use_gtid_value to remember users last choice and use this as default when connecting to slave. Replace mi->using_gtid, with was a pointer to master_use_gtid, with two functions: - using_gtid() returns the current mode, for the code that needs the value itself (SHOW SLAVE STATUS and performance schema output, comparisons against CURRENT_POS and SLAVE_POS). - is_gtid_supported() tells whether the connection uses gtid at all. The many callers that compared the mode against USE_GTID_NO with == or != now call this function instead of testing the enum value. - Master_use_gtid_value::supported() did the same test as is_gtid_supported() and is removed - Removed some operators in rpl_master_info_file.* that hid that functions was used to read and update values. Behavior changes: - The last used value in CHANGE MASTER for MASTER_USE_GTID will be remembered for future CHANGE MASTER for the same connection. This is true even if RESET SLAVE is called. - If Using git is not used with change master but log_file_name, pos, relay_log_name or relay_log_pos is specified this is treated as if Using_git=NO would have been given. (Old behavior, but not documented). --auto-master-use-gtid does nothing. The default value of master_use_gtid is by default SlavePos that will automatically change to NO if the master does not support GTID. This already handles what auto-master-use-gtid tries to do. - CHANGE MASTER's MASTER_HEARTBEAT_PERIOD now works like other MASTER options: When set to a value, this value is kept until value is changed or set to DEFAULT. The DEFAULT is @global.master_heartbeat_period if this is set as startup, if not then @global.slave_net_timeout/2 is used. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Read sole now-partition for non-historical versioned query When a versioned table stores its history in partitions, an implicit query (no FOR SYSTEM_TIME) needs only current rows, which all live in the current partition. vers_setup_conds() no longer adds a row_end condition for such a table, and prune_partitions() restricts read_partitions to the current partition instead. read_partitions is reset to all partitions every statement in open_table(), so the restriction must be applied on each execution. prune_partitions() runs on each execution, also for merged derived tables and views, where vers_setup_conds() is not called again. Both functions guard with !vers_conditions.was_set(). In prune_partitions() !is_set() would not do: set_all() sets type (flips is_set()) but not orig_type (was_set()), so is_set() would be true on prepared-statement / stored-procedure re-executions and skip the restriction, leaving history partitions readable - rows matched then grow on each execute/call. A query whose time comes from @@system_versioning_asof must still read history, so the restriction is also skipped when from_sysvar is set. init_from_sysvar() sets this new flag rather than orig_type, so was_set(), print() and the write lock check still see only an explicit FOR SYSTEM_TIME. Like orig_type, the flag persists to prepared-statement re-execution. EXPLAIN no longer shows "Using where" for such a query: correctness now comes from partition selection rather than a row_end filter. With sql_safe_updates, DELETE without WHERE on such a table is now rejected even with LIMIT, as for a non-versioned table. With an index on row_end, UPDATE or DELETE without WHERE (UPDATE also without LIMIT) is now rejected too: it was accepted as a range scan on row_end. A versioned table without SYSTEM_TIME partitions keeps the old behavior (MDEV-41393). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Refactor prune_partitions() to build into a dedicated bitmap prune_partitions() built the used-partition set directly in read_partitions: it cleared it, let find_used_partitions*() fill it, and in the index-merge case used it as scratch too - both buffer and result. The patch adds PART_PRUNE_PARAM::parts_bitmap and builds the return into it instead. read_partitions is produced from it only at the end: copied when the condition was analyzed (IMPOSSIBLE included, parts_bitmap left empty), or set to all locked partitions via mark_all_partitions_as_used() when nothing could be pruned. Pure refactoring: read_partitions ends up identical on every path. It decouples the pruning scratch from read_partitions, so a later commit can switch the final bitmap_copy() to bitmap_intersect() - letting pruning narrow a pre-set read_partitions (e.g. a versioned table selecting only its current partition) instead of overwriting it. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Have mtr also use /usr/lib64/libgalera_smm.so when searching for wsrep lib | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41146: Merge pseudo_bits_cond into outer_ref_cond A join of only const tables stored the WHERE conjuncts that refer to a column of an outer query or call rand() in pseudo_bits_cond. The executor read pseudo_bits_cond only when the query needed no temporary table, and read outer_ref_cond otherwise. A window function or DISTINCT with ROLLUP needs a temporary table, so those conjuncts were never checked, and the query returned rows that the WHERE clause rejects. pseudo_bits_cond came from the fix for LP bug 908269 in 2012. For a join of only const tables, that fix also collected conjuncts that call rand(), and it stored them in a new field without stating a reason. The code that filled the two fields and the code that read them chose between them by the same test. The tests have differed since MDEV-8646 in 2016, which added the temporary table condition to the executor's test. Later changes only display the field, in EXPLAIN FORMAT=JSON (MDEV-19714) and in the optimizer trace (MDEV-21095). In MDEV-19714, its author wrote that it was not clear why pseudo_bits_cond was needed in addition to outer_ref_cond. pseudo_bits_cond is removed, and outer_ref_cond holds these conjuncts for every join. The executor checks it on both paths, so where the conjuncts are stored no longer depends on the executor's test. Conjuncts that call rand() are still collected only for a join of only const tables. EXPLAIN FORMAT=JSON shows these conjuncts as outer_ref_condition instead of pseudo_bits_condition. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Alessandro Vetere
iminelink@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-32286 Reuse remembered clustered leaves in secondary-index scans Row_sel_get_clust_rec_for_mysql::operator() descends the clustered B-tree from the root for every row whose clustered-index record a secondary-index scan must read, although consecutive rows land on the same clustered leaf page wherever the secondary order tracks the clustered one. A non-covering scan needs one for every row, a locking read needs one whatever the secondary index holds, because an exclusive select lock type makes ha_innobase::build_template() build its template against the clustered index, and a covering scan needs one for every row of a secondary leaf whose PAGE_MAX_TRX_ID its read view cannot see. ANALYZE FORMAT=JSON charges each descent its full height, and those descents are nearly the whole cost: the secondary index is charged its own descent and one page for each further leaf, and nothing per row, because the position that its cursor holds between two rows is restored optimistically, which latches the leaf again without counting an access. So pages_accessed is the row count times the height of the clustered index, plus a handful: 1000 rows over a 2-level clustered index cost 2006 and 750 rows over a 3-level one cost 2291, where a full table scan of the same data costs 23 and 110. Let a handle remember the clustered leaves that the lookups of one statement reached, and let the next lookup try them before it descends again. The reasoning behind each value and each rejection is in the comments beside it. row0mysql.h defines clust_leaf_hint_slot, which names one leaf: its page number, copies of its first and last user record truncated to the key fields, which bound the key range that the leaf held when it was remembered, and the rec_get_offsets() of both. A slot of a leaf that had no right sibling names no last key, because every key above the last record of the rightmost leaf still belongs to it. CLUST_LEAF_HINT_SLOTS (4) slots hang off the new row_prebuilt_t::clust_leaf_hint, beside clust_leaf_hint_mru, the most recently used order held as slot numbers, and clust_leaf_hint_n and clust_leaf_hint_miss, the used-slot count and the miss counter. row0sel.cc holds the policy. row_sel_clust_leaf_hint_covers() compares a key against the remembered ranges, so a lookup that no slot can answer costs no buffer pool access and no pages_accessed. row_sel_clust_leaf_hint_search() probes the first slot that covers the key and moves it to the front of the order. row_sel_clust_leaf_hint_remember() records the leaf that a descent landed on, and refreshes the slot of a leaf that is remembered already rather than spend a second one on the same page. Two descents fill no slot: the lookups of a statement before lookup CLUST_LEAF_HINT_MIN_LOOKUPS (4), and a leaf that is the root. row_sel_clust_leaf_hint_armed() stands a scan down once the slots stop paying for themselves: a miss adds CLUST_LEAF_HINT_MISS_WEIGHT (2) to the miss counter and a hit takes one away, so a scan gives the slots up where it answers too little of its lookups to pay for them, CLUST_LEAF_HINT_MAX_MISSES (8) misses with no hit between them still reach the threshold, and one lookup in CLUST_LEAF_HINT_RETRY (1024) starts the count again, so a scan whose order becomes correlated only later recovers. Both halves of the cost stop there, the test of the slots and the copies that refresh them. Row_sel_get_clust_rec_for_mysql::operator() calls all of this in place of its btr_pcur_open_with_no_init(), and only where the adaptive hash index is disabled. A guess of that index lands on the record with no page-local search and no page access to charge, but neither of the two is faster for every shape of index and scan, and a hint that runs first stops that index from adding entries. The adaptive hash index is off by default, so the hints are active in a default configuration. btr0cur.h and btr0cur.cc add btr_cur_t::try_leaf_hint(), a PAGE_CUR_LE, BTR_SEARCH_LEAF search on one named leaf, which starts from the record where the last search of that leaf landed while that record is still valid, and from a binary search of the page otherwise. It acquires the page with buf_page_try_get(): a hint is never derived from a latched parent page, so by the time it is tried it can precede the caller's already-latched secondary-index leaf in the latching order, where a blocking wait can deadlock. It then rejects the page unless the checks that it makes on the latched frame put the match on it. Those checks are the sole authority on the result, so a stale range costs a wasted probe or a needless descent, never a wrong result, and the ranges need no invalidation protocol. A slot also holds a btr_leaf_step, where the last search of its leaf landed: the block, its buf_block_t::modify_clock and the record. A record pointer stays valid while the block holds the same page and the clock has not moved, because eviction, deletion and reorganization advance the clock and an insertion moves no record. While the step is valid, btr_cur_t::try_leaf_hint() starts from that record instead of a binary search of the page. page0cur.cc adds page_cur_search_near(), which compares the key with the record and with one neighbour, and positions the cursor on the record, on the record after it or on the record before it. It refuses any other answer and leaves it to page_cur_search_with_match(). So a scan whose consecutive rows are neighbours in the clustered index finds each row with two comparisons. A scan that reads the clustered leaf in falling key order does the same: a secondary key that falls as the primary key rises, or a secondary index read backward. Where the secondary order tracks the clustered one with gaps between the rows, the step fails, and btr_leaf_step::expect (below) leaves the leaf to the binary search. The step refuses the metadata pseudo-record, which compares below every key on 0 fields, and a record before it that is the infimum. A debug build checks every successful step against the binary search. A failed step costs comparisons whose outcome no branch predictor can guess, so btr_leaf_step::expect gates it. The next search tries the step only where this one landed where a step would have: on the remembered record, or right before or after it. The successor link of the landing record tells the record right before, because finding a predecessor takes a walk of the page directory. So a scan in random order over a few leaves, which the slots answer in full, keeps the binary search and pays only the tests that set expect. row_sel_clust_leaf_hint_remember() sets the step on the record where a descent landed, with expect set. ha_innodb.cc: ha_innobase::reset() zeroes the used-slot count and the miss counter per statement, matching autoinc_last_value. row0mysql.cc: row_prebuilt_free() frees the key buffers that the slots own. innodb.clust_leaf_hint measures pages_accessed over key orders that differ in how closely the secondary order tracks the clustered one, and further tables check query results over the record formats and key shapes that a clustered-index lookup has to read, down to the metadata pseudo-record of instant ALTER TABLE, to leaves that split and merge while a locking read walks them, and to a record that a remembered leaf supplies for a scan that must then rebuild an older version of it. Two of the tables scan a covering index, which reads a clustered record under an exclusive select lock type and under a PAGE_MAX_TRX_ID that the read view cannot see. clust_leaf_hint_off_debug runs the same body with the hints turned off, through a debug switch that returns before a lookup tests or refreshes the slots, so a diff of the two .result files is what the hints save: 2006 to 1031 (2-level clustered index), 2291 to 1011 (3-level), 4006 to 2015 (two interleaved key ranges), 12016 to 9078 (locality in the second half alone) and 20020 to 10045 for a covering scan that FOR UPDATE makes non-covering, where the same scan without FOR UPDATE costs 20 in both files. Two orders with too little locality to pay for the slots give them up early and end within a hundred accesses of the unhinted count: 20020 to 19966 (decorrelated) and 20020 to 19999 (shuffled). Two further tables in innodb.clust_leaf_hint cover the step back. t16 scans a secondary index whose keys fall as the primary key rises, and reads an ascending secondary index backward: 20020 to 10047 for both. t17 runs the inverse scan on ROW_FORMAT=REDUNDANT with an instantly added column. innodb.clust_leaf_hint_backward_debug reaches the two refusals of the step back that no count shows, at the infimum and at the metadata pseudo-record. A lookup reaches either only for a key that left the clustered index while the slot still covers it. The rollback of an insert over a delete-marked record removes the key while purge leaves the secondary entry, and a PAGE_MAX_TRX_ID that the read view cannot see makes the reader look that entry up. A debug switch writes each refusal to the error log, and the case reads both back. A slot stays valid when dict_index_t::clear_instant_alter() changes the index under a reader that holds a shared metadata lock, between two lookups or during one. That clear runs where purge or rollback removes the last user record of a single-page table whose only instant change was ADD COLUMN, and it raises dict_index_t::n_core_fields. rec_copy_prefix_to_buf() writes the copies in the format of an index without instant ADD COLUMN, their offsets are made with them, and the comparisons depend on the key fields alone, which no instant change moves. So the slots do not record n_core_fields, and the hints read it only under a latch on a clustered leaf. innodb.clust_leaf_hint_instant_alter makes a lookup compare against a slot that a clear has outlived. The clear needs the clustered index to lose the last user record of its root page, while no leaf that is the root fills a slot, so the tree has to shrink between the two, which purge does there. The reader therefore reads uncommitted rows at READ UNCOMMITTED and waits in a stored function while a rollback and purge take them away, and one row that arrives above the position it stopped at is the lookup that tests the slots. main.rowid_filter_innodb: 90 to 88, and its ahi combination unchanged. A static_assert in Row_sel_get_clust_rec_for_mysql::operator() fails to compile if btr_search.enabled stops being a Boolean that applies to every index, because the test !btr_search.enabled that stands the hints down would then have to become !btr_search.is_enabled(clust_index). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 bitmap_range() for iteration over partition bitmap Returns bit range for partition element. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Pekka Lampio
pekka.lampio@galeracluster.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39143 follow-up: fix review findings in MySQL JSON replication Addresses issues found in review of 16ce07df15d (Replicate MySQL binary JSON to a MariaDB slave), in both the new code and two pieces of pre-existing core replication code it interacts with. Issue numbers below refer to the review's own numbering (I001-I039). Crashes and silent corruption: - A truncated or malformed Json_diff vector in a PARTIAL_UPDATE_ROWS_EVENT could NULL-deref the slave: safe_net_field_length_ll() nulls its cursor on a truncated encoding, and nothing checked for that. (I001, I002) - The same unchecked-cursor bug existed in the after-image header parser, which additionally had no bound on partial_bits consumption and did not reject out-of-range value_options. (I005) - A diff that MySQL's own applier would REJECT (its path doesn't match the slave's document) was silently applied as a no-op instead of halting replication - the opposite of MySQL's own behavior (ER_COULD_NOT_APPLY_JSON_DIFF). (I004, I025) - Corrupt MySQL binary JSON in a diff value was committed as a JSON null instead of being detected: Field_mysql_json::val_str() pushes an error but returns a non-null empty string on failure, so the previous `if (!vtext)` check could never fire. (I003) - The same silent-corruption class existed on the full-document conversion path for a native-JSON target. (I038) - find_row() failing to locate the matched row still read the base document from stale record[1] data, and did not record the correct stream position on error, risking a parse desync under slave_exec_mode=IDEMPOTENT. (I020, I033) Error reporting: - Most failure paths in apply_partial_json_column() returned a bare HA_ERR_GENERIC with nothing reported to the DBA, contradicting the function's own documented contract. (I024, I026, I027, I031) - Fixed messages use my_printf_error() rather than rgi->rli->report(): do_exec_row()'s caller unconditionally re-derives Last_SQL_Error from thd->is_error() once a row event returns an error, overwriting anything rgi->rli->report() had already recorded with a generic "Got error NNN from storage engine" wrapper - only a message already in the diagnostics area survives. - check_wrong_column_usage() only checked an UPDATE event's before-image bitmap, so an incompatible column visible only in the after-image (binlog_row_image=MINIMAL) was silently dropped from the write set instead of being reported. (I006) - slave.cc's IO-thread statement-end tracking was missing PARTIAL_UPDATE_ROWS_EVENT, which could produce a false-positive "did not receive an expected Rows-log end-of-statement" error. (I008) - Documented, but did not fix (needs generic plugin refcounting, out of scope here): Type_handler::handler_by_name() releases its plugin lock before the caller is done using the returned handler, a pre-existing race in the data-type plugin framework. (I039) Design/consistency: - A MySQL JSON source converting to a plain string/blob target was always classified as safely widening regardless of actual size; it is now classified by the target's real declared capacity like any other blob/string conversion, so a document that doesn't fit requires slave_type_conversions=ALL_LOSSY instead of silently truncating under ALL_NON_LOSSY alone. (I007) - apply_partial_json_column() now builds its per-diff JSON function Items on a private MEM_ROOT freed per row/column, instead of thd->mem_root (the statement-level arena), avoiding unbounded growth on a large multi-row UPDATE. (I028) - An open-coded bitmap test duplicating the existing rpl_bitmap_is_set() helper was replaced with a call to it. (I032) - Corrected a comment overstating MySQL JSON -> native JSON conversion as lossless; a JSONB OPAQUE temporal/decimal value loses its original type tag even though its content survives. (I036) - Deduplicated the "MYSQL_JSON" plugin-name literal into one constant. (I037) Tests: - Switched the existing tests from unconditional INSTALL/UNINSTALL SONAME to the repo's have_type_mysql_json.inc convention. (I013) - Corrected a test comment that misattributed why the from-the-end array-index diff is refused. (I014) - Extracted the tests' duplicated binlog-fixture-injection and post-replication resync logic into shared include files. (I015) - Added tests for the missing-plugin, conversion-length-gating, and corrupt-diff-vector error paths above. (I016) Assisted-by: Claude AI |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Avoid strlen() calls in Explain_index_usage and String_list Use Lex_ident_column* instead of char* as parameters |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Oleg Smirnov
olernov@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Pushdown from HAVING: Fail the query on errors The pushdown from HAVING into WHERE modifies HAVING before all of its steps have succeeded. On a failure it emptied the list of pushed conditions and let the query continue, so the conditions already removed from HAVING were not checked at all. pushdown_from_having_into_where() now returns an error status, and the query errors out if the pushdown fails after it may have modified HAVING. thd->lex->current_select is now restored on all return paths. It was not restored when collecting the grouping fields failed. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-30295 mysqldump produces syntactically incorrect statement cover_definer_clause() wrapped DEFINER in "*/ /*!50017 ... */" and broke when the statement text contained a comment. Dump the server's DDL as returned. Also: - drop /*!NNNNN */ gates below 50700 in mysqldump, mysqlbinlog, mysqladmin, mysqlcheck, mysqlshow, mysqlimport, mysqlhotcopy, innotop and mtr_warnings.sql; mysqlbinlog's skip_replication becomes /*M! ...*/ - SHOW CREATE DATABASE no longer wraps IF NOT EXISTS and DEFAULT CHARACTER SET in comments - remove checks for servers older than 5.7 in mysqldump, mysqlcheck, mysql, innotop and the client library, including dump_trigger_old() and mysqlcheck's SHOW TABLES fallback - mysqldump warns on stderr if SHOW CREATE DATABASE fails Behavior changes: - --compatible=mysql323 and mysql40 are accepted and ignored. The other modes wrap only some SET and ALTER TABLE ... KEYS statements in /*!40101 */. - mysqlhotcopy --old_server is accepted, ignored, and prints a warning. Assisted-By: Claude:claude-5.5-sonnet |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Refactor prune_partitions() to narrow read_partitions instead of full rewrite read_partitions is reset to all partitions (or the PARTITION() set) once per statement in open_table() (set_partition_bitmaps()), and prune_partitions() only intersects it with lock_partitions and the pruned set instead of rebuilding it. mark_all_partitions_as_used() is dropped. Pure refactoring, results unchanged. It lets a restriction set on read_partitions before pruning survive it - needed when read_partitions is pre-restricted, e.g. a versioned table whose history is partitioned reads only the current partition. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-30295 mysqldump produces syntactically incorrect statement cover_definer_clause() wrapped DEFINER in "*/ /*!50017 ... */" and broke when the statement text contained a comment. Dump the server's DDL as returned. Also: - drop /*!NNNNN */ gates below 50700 in mysqldump, mysqlbinlog, mysqladmin, mysqlcheck, mysqlshow, mysqlimport, mysqlhotcopy, innotop and mtr_warnings.sql; mysqlbinlog's skip_replication becomes /*M! ...*/ - SHOW CREATE DATABASE no longer wraps IF NOT EXISTS and DEFAULT CHARACTER SET in comments - remove checks for servers older than 5.7 in mysqldump, mysqlcheck, mysql, innotop and the client library, including dump_trigger_old() and mysqlcheck's SHOW TABLES fallback - mysqldump warns on stderr if SHOW CREATE DATABASE fails Behavior changes: - --compatible=mysql323 and mysql40 are accepted and ignored. The other modes wrap only some SET and ALTER TABLE ... KEYS statements in /*!40101 */. DEFINER, DROP ... IF EXISTS, CREATE VIEW/TRIGGER/EVENT and FLUSH PRIVILEGES are no longer wrapped, so scripts matching /*!50017 DEFINER=... */ stop matching. Assisted-By: Claude:claude-5.5-sonnet |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Test cleanup: sorted result for versioning.partition Avoid result fluctuations with different storage engines. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Add likely/unlikely to my_b_read/write functions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Remove wrong assert in log_event_server.cc The test for: DBUG_ASSERT(thd->lex->query_tables != table_list); does not make any sense: - table_list is a pointer we just got from malloc)() - thd->lex->query_tables can point to an existing TABLE_LIST or is a pointer to already freed memory. In 'main' I got the above assert multiple times triggered when running rpl.rpl_row_img_blobs on a valgrind build. There was nothing wrong with the allocations, just random change have malloc return a recently freed pointer. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-39173: Use snprintf in the remaining sprintf calls Convert all three calls to snprintf. 12.3 already has the same change for slave.cc, made in merge commit 9f98f82b14a. MDEV-39173 replaced sprintf with snprintf in 10.11 and removed the pragma in violite.h that hid deprecation warnings. Two calls in slave.cc and one in mysqltest.cc were added after 10.11, so 11.4 was out of date and produces sprintf deprecation warnings (at least on macOS). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Remove warnings from aria_chk - Added right path to datadir where aria_log_control is - Added option --ignore-control-file as the server is keeping it locked. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Fixed wrong code in ha_perfschema::delete_table() In case there was no '/' or '\' in the path, *ptr = '\0' would change data outside of table_path[] This patch also removes a warning from gcc. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41146: Merge pseudo_bits_cond into outer_ref_cond A join of only const tables stored the WHERE conjuncts that refer to a column of an outer query or call rand() in pseudo_bits_cond. The executor read pseudo_bits_cond only when the query needed no temporary table, and read outer_ref_cond otherwise. A window function or DISTINCT with ROLLUP needs a temporary table, so those conjuncts were never checked, and the query returned rows that the WHERE clause rejects. pseudo_bits_cond came from the fix for LP bug 908269 in 2012. For a join of only const tables, that fix also collected conjuncts that call rand(), and it stored them in a new field without stating a reason. The code that filled the two fields and the code that read them chose between them by the same test. The tests have differed since MDEV-8646 in 2016, which added the temporary table condition to the executor's test. Later changes only display the field, in EXPLAIN FORMAT=JSON (MDEV-19714) and in the optimizer trace (MDEV-21095). In MDEV-19714, its author wrote that it was not clear why pseudo_bits_cond was needed in addition to outer_ref_cond. pseudo_bits_cond is removed, and outer_ref_cond holds these conjuncts for every join. The executor checks it on both paths, so where the conjuncts are stored no longer depends on the executor's test. Conjuncts that call rand() are still collected only for a join of only const tables. EXPLAIN FORMAT=JSON shows these conjuncts as outer_ref_condition instead of pseudo_bits_condition. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Fixed bug in rpl.rpl_killed_ddl.result test case View 'v1' was not always dropped (timeing issue) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41146: Check rand() in WHERE when top level base tables are const A WHERE conjunct that calls rand() but doesn't refer to a column, such as rand() < 0, is checked on the last non-const top level base table in the join order. A join of only const tables has no such table and returns at most one row, so the conjunct is checked once before the join starts, together with the conditions on columns of an outer query. When every top level base table is const and materialized semi-joins follow them, the join order also has no non-const top level base table, but only_const_tables() is false. The conjunct was never checked, and such a query gave wrong results. The check before the join now applies whenever the join order has no non-const top level base table, which is the same condition under which no table in the join order receives the conjunct. Such a join also returns at most one row. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
forkfun
alice.sherepa@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-30295 mysqldump produces syntactically incorrect statement cover_definer_clause() wrapped DEFINER in "*/ /*!50017 ... */" and broke when the statement text contained a comment. Dump the server's DDL as returned. Also: - drop /*!NNNNN */ gates below 50700 in mysqldump, mysqlbinlog, mysqladmin, mysqlcheck, mysqlshow, mysqlimport, mysqlhotcopy and innotop; mysqlbinlog's skip_replication becomes /*M! ...*/ - SHOW CREATE DATABASE no longer wraps IF NOT EXISTS and DEFAULT CHARACTER SET in comments - remove old-server checks: dump_trigger_old(), mysqlcheck's SHOW TABLES fallback - mysqldump warns on stderr if SHOW CREATE DATABASE fails Behavior changes: - --compatible=mysql323 and mysql40 are accepted and ignored. The other modes wrap only some SET and ALTER TABLE ... KEYS statements in /*!40101 */. DEFINER, DROP ... IF EXISTS, CREATE VIEW/TRIGGER/EVENT and FLUSH PRIVILEGES are no longer wrapped, so scripts matching /*!50017 DEFINER=... */ stop matching. Assisted-By: Claude:claude-5.5-sonnet |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Monty
monty@mariadb.org |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Remove warnings from aria_chk - Added right path to datadir where aria_log_control is - Added option --ignore-control-file as the server is keeping it locked. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dave Gosselin
dave.gosselin@mariadb.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-41146: Check rand() in WHERE when top level base tables are const A WHERE conjunct that calls rand() but doesn't refer to a column, such as rand() < 0, is checked on the last non-const top level base table in the join order. A join of only const tables has no such table and returns at most one row, so the conjunct is checked once before the join starts, together with the conditions on columns of an outer query. When every top level base table is const and materialized semi-joins follow them, the join order also has no non-const top level base table, but only_const_tables() is false. The conjunct was never checked, and such a query gave wrong results. The check before the join now applies whenever the join order has no non-const top level base table, which is the same condition under which no table in the join order receives the conjunct. Such a join also returns at most one row. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Aleksey Midenkov
midenok@gmail.com |
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MDEV-17613 Read sole now-partition for non-historical versioned query When a versioned table stores its history in partitions, an implicit query (no FOR SYSTEM_TIME) needs only current rows, which all live in the current partition. vers_setup_conds() no longer adds a row_end condition for such a table, and prune_partitions() restricts read_partitions to the current partition instead. read_partitions is reset to all partitions every statement in open_table(), so the restriction must be applied on each execution. prune_partitions() runs on each execution, also for merged derived tables and views, where vers_setup_conds() is not called again. Both functions guard with !vers_conditions.was_set(). In prune_partitions() !is_set() would not do: set_all() sets type (flips is_set()) but not orig_type (was_set()), so is_set() would be true on prepared-statement / stored-procedure re-executions and skip the restriction, leaving history partitions readable - rows matched then grow on each execute/call. A query whose time comes from @@system_versioning_asof must still read history, so the restriction is also skipped when from_sysvar is set. init_from_sysvar() sets this new flag rather than orig_type, so was_set(), print() and the write lock check still see only an explicit FOR SYSTEM_TIME. Like orig_type, the flag persists to prepared-statement re-execution. EXPLAIN no longer shows "Using where" for such a query: correctness now comes from partition selection rather than a row_end filter. With sql_safe_updates, DELETE without WHERE on such a table is now rejected even with LIMIT, as for a non-versioned table. With an index on row_end, UPDATE or DELETE without WHERE (UPDATE also without LIMIT) is now rejected too: it was accepted as a range scan on row_end. A versioned table without SYSTEM_TIME partitions keeps the old behavior (MDEV-41393). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||