From f6542a7af8d641ab3d1edfe0372c2b55a97a5633 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 1 Apr 2021 23:01:19 +0200 Subject: [PATCH 01/14] Update timezone data on Windows There is new Yukon Standard time Windows timezone. Also fix the powershell script that generates the Windows locale mapping, tell powershell to use TLSv1.2 to access the github (on some reason it is TLS1.1 that powershell is using by default, and it does no work) --- sql/gen_win_tzname_data.ps1 | 1 + sql/win_tzname_data.h | 1 + 2 files changed, 2 insertions(+) diff --git a/sql/gen_win_tzname_data.ps1 b/sql/gen_win_tzname_data.ps1 index c0a37d21895..474ab889d25 100644 --- a/sql/gen_win_tzname_data.ps1 +++ b/sql/gen_win_tzname_data.ps1 @@ -4,6 +4,7 @@ write-output "/* This file was generated using gen_win_tzname_data.ps1 */" $xdoc = new-object System.Xml.XmlDocument +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $xdoc.load("https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml") $nodes = $xdoc.SelectNodes("//mapZone[@territory='001']") # use default territory (001) foreach ($node in $nodes) { diff --git a/sql/win_tzname_data.h b/sql/win_tzname_data.h index 03197227f8e..792cdbc7a13 100644 --- a/sql/win_tzname_data.h +++ b/sql/win_tzname_data.h @@ -12,6 +12,7 @@ {L"US Mountain Standard Time","America/Phoenix"}, {L"Mountain Standard Time (Mexico)","America/Chihuahua"}, {L"Mountain Standard Time","America/Denver"}, +{L"Yukon Standard Time","America/Whitehorse"}, {L"Central America Standard Time","America/Guatemala"}, {L"Central Standard Time","America/Chicago"}, {L"Easter Island Standard Time","Pacific/Easter"}, From 78bb9533f4a96fc7b089436da46bcd0ae4366bab Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 22 Apr 2021 15:49:00 +0200 Subject: [PATCH 02/14] MDEV-25456 MariaBackup logs "[ERROR]" on Invalid log block checksum Fix is to changed message to be [WARNING] for backup --- storage/innobase/log/log0recv.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 42b8e4dee88..fa9ec8e642f 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -967,7 +967,8 @@ fail: DBUG_EXECUTE_IF("log_checksum_mismatch", { cksum = crc + 1; }); if (crc != cksum) { - ib::error() << "Invalid log block checksum." + ib::error_or_warn(srv_operation == SRV_OPERATION_BACKUP) + << "Invalid log block checksum." << " block: " << block_number << " checkpoint no: " << log_block_get_checkpoint_no(buf) From 5c5d24c772d4596b0c28f2267477afe448d6e7f7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 22 Apr 2021 16:59:30 +0200 Subject: [PATCH 03/14] MDEV-25456 - fix predicate in ib::error_or_warn --- storage/innobase/log/log0recv.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index fa9ec8e642f..436633185b3 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -967,7 +967,7 @@ fail: DBUG_EXECUTE_IF("log_checksum_mismatch", { cksum = crc + 1; }); if (crc != cksum) { - ib::error_or_warn(srv_operation == SRV_OPERATION_BACKUP) + ib::error_or_warn(srv_operation != SRV_OPERATION_BACKUP) << "Invalid log block checksum." << " block: " << block_number << " checkpoint no: " From a40f29ab1933af9b7d21398136f0889d4bb3e9b0 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 20 Apr 2021 16:53:56 +0200 Subject: [PATCH 04/14] MYSQL_MAINTAINER_MODE fixes * MSVC check was lost in a bad merge * add a comment * move AIX check where it belongs and where it would still allow -Werror, if desired --- cmake/maintainer.cmake | 9 +++------ cmake/os/AIX.cmake | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake index 0df943be863..b2b2eef9245 100644 --- a/cmake/maintainer.cmake +++ b/cmake/maintainer.cmake @@ -14,7 +14,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA -IF(MYSQL_MAINTAINER_MODE STREQUAL "NO") +IF(MSVC OR MYSQL_MAINTAINER_MODE STREQUAL "NO") + # Windows support is in cmake/os/Windows.cmake, not here RETURN() ENDIF() @@ -40,11 +41,7 @@ FOREACH(F ${MY_WARNING_FLAGS}) MY_CHECK_AND_SET_COMPILER_FLAG(${F} DEBUG RELWITHDEBINFO) ENDFOREACH() -IF(CMAKE_SYSTEM_NAME MATCHES AIX) - SET(MY_ERROR_FLAGS "") -ELSE() - SET(MY_ERROR_FLAGS -Werror) -ENDIF() +SET(MY_ERROR_FLAGS -Werror) IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS "6.0.0") SET(MY_ERROR_FLAGS ${MY_ERROR_FLAGS} -Wno-error=maybe-uninitialized) diff --git a/cmake/os/AIX.cmake b/cmake/os/AIX.cmake index bd4c2a1df97..299b79198c6 100644 --- a/cmake/os/AIX.cmake +++ b/cmake/os/AIX.cmake @@ -33,3 +33,6 @@ ELSE() SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -maix64 -pthread -mcmodel=large") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -maix64 -pthread -mcmodel=large") ENDIF() + +# make it WARN by default, not AUTO (that implies -Werror) +SET(MYSQL_MAINTAINER_MODE "WARN" CACHE STRING "Enable MariaDB maintainer-specific warnings. One of: NO (warnings are disabled) WARN (warnings are enabled) ERR (warnings are errors) AUTO (warnings are errors in Debug only)") From b8fad8c6bffbc7e43796fffe607b23075cfc3e2b Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 21 Apr 2021 12:51:39 +0200 Subject: [PATCH 05/14] MDEV-25030 Upgrade to 10.5.9 breaks root's ability to grant --- mysql-test/main/grant_slave_monitor.result | 20 ++++++++++++++++++++ mysql-test/main/grant_slave_monitor.test | 16 ++++++++++++++++ sql/privilege.h | 3 +++ sql/sql_acl.cc | 13 +++++++++---- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/grant_slave_monitor.result b/mysql-test/main/grant_slave_monitor.result index ea0b7d8094c..68df790074a 100644 --- a/mysql-test/main/grant_slave_monitor.result +++ b/mysql-test/main/grant_slave_monitor.result @@ -53,3 +53,23 @@ ERROR 42000: Access denied; you need (at least one of) the SLAVE MONITOR privile disconnect con1; connection default; DROP USER user1@localhost; +# +# MDEV-25030 Upgrade to 10.5.9 breaks root's ability to grant +# +insert mysql.global_priv values ('bar', 'foo7', '{"access":274877906943,"version_id":100507,"plugin":"mysql_native_password","authentication_string":""}'), +('bar', 'foo8', '{"access":274877906943,"version_id":100508,"plugin":"mysql_native_password","authentication_string":""}'), +('bar', 'foo9', '{"access":274877906943,"version_id":100509,"plugin":"mysql_native_password","authentication_string":""}'); +flush privileges; +show grants for foo7@bar; +Grants for foo7@bar +GRANT ALL PRIVILEGES ON *.* TO `foo7`@`bar` WITH GRANT OPTION +show grants for foo8@bar; +Grants for foo8@bar +GRANT ALL PRIVILEGES ON *.* TO `foo8`@`bar` WITH GRANT OPTION +show grants for foo9@bar; +Grants for foo9@bar +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, BINLOG MONITOR, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, DELETE HISTORY, SET USER, FEDERATED ADMIN, CONNECTION ADMIN, READ_ONLY ADMIN, REPLICATION SLAVE ADMIN, REPLICATION MASTER ADMIN, BINLOG ADMIN, BINLOG REPLAY ON *.* TO `foo9`@`bar` WITH GRANT OPTION +drop user foo7@bar, foo8@bar, foo9@bar; +# +# End of 10.5 tests +# diff --git a/mysql-test/main/grant_slave_monitor.test b/mysql-test/main/grant_slave_monitor.test index 7dacaa36aee..af4399b7150 100644 --- a/mysql-test/main/grant_slave_monitor.test +++ b/mysql-test/main/grant_slave_monitor.test @@ -99,3 +99,19 @@ SHOW RELAYLOG EVENTS; --connection default DROP USER user1@localhost; + +--echo # +--echo # MDEV-25030 Upgrade to 10.5.9 breaks root's ability to grant +--echo # +insert mysql.global_priv values ('bar', 'foo7', '{"access":274877906943,"version_id":100507,"plugin":"mysql_native_password","authentication_string":""}'), + ('bar', 'foo8', '{"access":274877906943,"version_id":100508,"plugin":"mysql_native_password","authentication_string":""}'), + ('bar', 'foo9', '{"access":274877906943,"version_id":100509,"plugin":"mysql_native_password","authentication_string":""}'); +flush privileges; +show grants for foo7@bar; +show grants for foo8@bar; +show grants for foo9@bar; +drop user foo7@bar, foo8@bar, foo9@bar; + +--echo # +--echo # End of 10.5 tests +--echo # diff --git a/sql/privilege.h b/sql/privilege.h index a2e75003c11..3e4c2526c6c 100644 --- a/sql/privilege.h +++ b/sql/privilege.h @@ -121,6 +121,9 @@ constexpr privilege_t ALL_KNOWN_ACL_100502= ALL_KNOWN_BITS(LAST_100502_ACL); // A combination of all bits defined in 10.5.8 constexpr privilege_t ALL_KNOWN_ACL_100508= ALL_KNOWN_BITS(LAST_100508_ACL); +// unfortunately, SLAVE_MONITOR_ACL was added in 10.5.9, but also in 10.5.8-5 +// let's stay compatible with that branch too. +constexpr privilege_t ALL_KNOWN_ACL_100509= ALL_KNOWN_ACL_100508; // A combination of all bits defined as of the current version constexpr privilege_t ALL_KNOWN_ACL= ALL_KNOWN_BITS(LAST_CURRENT_ACL); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index bba875d883c..a7fbdefd073 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1531,13 +1531,18 @@ class User_table_json: public User_table { privilege_t mask= ALL_KNOWN_ACL_100304; ulonglong orig_access= access; - if (version_id >= 100508) + if (version_id >= 100509) { - mask= ALL_KNOWN_ACL_100508; + mask= ALL_KNOWN_ACL_100509; } - else if (version_id >= 100502 && version_id < 100508) + else if (version_id >= 100502) { - mask= ALL_KNOWN_ACL_100502; + if (version_id >= 100508) + mask= ALL_KNOWN_ACL_100508; + else + mask= ALL_KNOWN_ACL_100502; + if (access & REPL_SLAVE_ADMIN_ACL) + access|= SLAVE_MONITOR_ACL; } else // 100501 or earlier { From b3b5d57e78f835473d13d383caacb7320b5938d5 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 19 Apr 2021 19:52:06 -0700 Subject: [PATCH 06/14] MDEV-24823 Crash with invalid multi-table update of view in 2nd execution of SP Before this patch mergeable derived tables / view used in a multi-table update / delete were merged before the preparation stage. When the merge of a derived table / view is performed the on expression attached to it is fixed and ANDed with the where condition of the select S containing this derived table / view. It happens after the specification of the derived table / view has been merged into S. If the ON expression refers to a non existing field an error is reported and some other mergeable derived tables / views remain unmerged. It's not a problem if the multi-table update / delete statement is standalone. Yet if it is used in a stored procedure the select with incompletely merged derived tables / views may cause a problem for the second call of the procedure. This does not happen for select queries using derived tables / views, because in this case their specifications are merged after the preparation stage at which all ON expressions are fixed. This patch makes sure that merging of the derived tables / views used in a multi-table update / delete statement is performed after the preparation stage. Approved by Oleksandr Byelkin --- mysql-test/r/multi_update.result | 53 ++++++++++++++++++++++++++++++++ mysql-test/t/multi_update.test | 53 ++++++++++++++++++++++++++++++++ sql/sql_base.cc | 3 +- sql/sql_class.h | 4 ++- sql/sql_delete.cc | 15 ++++----- sql/sql_derived.cc | 5 +-- sql/sql_lex.cc | 21 +++++++++++++ sql/sql_lex.h | 2 ++ sql/sql_update.cc | 41 +++++++++++++++++------- 9 files changed, 171 insertions(+), 26 deletions(-) diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index a5c60d6dfc2..1532ebb6420 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -1043,3 +1043,56 @@ drop function f1; # # end of 5.5 tests # +# +# MDEV-24823: Invalid multi-table update of view within SP +# +create table t1 (id int) engine=myisam; +insert into t1 values (1),(2),(1); +create table t2 (pk int, c0 int) engine=myisam; +insert into t2 values (1,1), (2,3); +create view v2 as select * from t2; +create view v3 as select * from t2 where c0 < 3; +create procedure sp0() update t1, v2 set v2.pk = 1 where v2.c0 = t1.c1; +call sp0(); +ERROR 42S22: Unknown column 't1.c1' in 'where clause' +call sp0(); +ERROR 42S22: Unknown column 't1.c1' in 'where clause' +create procedure sp1() update (t1 join v2 on v2.c0 = t1.c1) set v2.pk = 1; +call sp1(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp1(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +create procedure sp2() update (t1 join v3 on v3.c0 = t1.c1) set v3.pk = 1; +call sp2(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp2(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +create procedure sp3() +update (t1 join v2 on v2.c0 = t1.id) set v2.c0 = v2.c0+1; +select * from t2; +pk c0 +1 1 +2 3 +call sp3(); +select * from t2; +pk c0 +1 2 +2 3 +call sp3(); +select * from t2; +pk c0 +1 3 +2 3 +create procedure sp4() delete t1 from t1 join v2 on v2.c0 = t1.c1; +call sp4(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp4(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +drop procedure sp0; +drop procedure sp1; +drop procedure sp2; +drop procedure sp3; +drop procedure sp4; +drop view v2,v3; +drop table t1,t2; +# End of 10.2 tests diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index b5328f74e70..dff3938d02f 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -1041,3 +1041,56 @@ drop function f1; --echo # --echo # end of 5.5 tests --echo # + +--echo # +--echo # MDEV-24823: Invalid multi-table update of view within SP +--echo # + +create table t1 (id int) engine=myisam; +insert into t1 values (1),(2),(1); +create table t2 (pk int, c0 int) engine=myisam; +insert into t2 values (1,1), (2,3); +create view v2 as select * from t2; +create view v3 as select * from t2 where c0 < 3; + +create procedure sp0() update t1, v2 set v2.pk = 1 where v2.c0 = t1.c1; +--error ER_BAD_FIELD_ERROR +call sp0(); +--error ER_BAD_FIELD_ERROR +call sp0(); + +create procedure sp1() update (t1 join v2 on v2.c0 = t1.c1) set v2.pk = 1; +--error ER_BAD_FIELD_ERROR +call sp1(); +--error ER_BAD_FIELD_ERROR +call sp1(); + +create procedure sp2() update (t1 join v3 on v3.c0 = t1.c1) set v3.pk = 1; +--error ER_BAD_FIELD_ERROR +call sp2(); +--error ER_BAD_FIELD_ERROR +call sp2(); + +create procedure sp3() +update (t1 join v2 on v2.c0 = t1.id) set v2.c0 = v2.c0+1; +select * from t2; +call sp3(); +select * from t2; +call sp3(); +select * from t2; + +create procedure sp4() delete t1 from t1 join v2 on v2.c0 = t1.c1; +--error ER_BAD_FIELD_ERROR +call sp4(); +--error ER_BAD_FIELD_ERROR +call sp4(); + +drop procedure sp0; +drop procedure sp1; +drop procedure sp2; +drop procedure sp3; +drop procedure sp4; +drop view v2,v3; +drop table t1,t2; + +--echo # End of 10.2 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f10846acdd7..3403f9e03ff 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7501,7 +7501,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (table_list->jtbm_subselect) { Item *item= table_list->jtbm_subselect->optimizer; - if (table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) + if (!table_list->jtbm_subselect->optimizer->fixed && + table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) { my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast(MAX_TABLES)); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/ DBUG_RETURN(1); diff --git a/sql/sql_class.h b/sql/sql_class.h index c47ea9c9020..ce4bf67e745 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5600,7 +5600,8 @@ public: class multi_update :public select_result_interceptor { TABLE_LIST *all_tables; /* query/update command tables */ - List *leaves; /* list of leves of join table tree */ + List *leaves; /* list of leaves of join table tree */ + List updated_leaves; /* list of of updated leaves */ TABLE_LIST *update_tables, *table_being_updated; TABLE **tmp_tables, *main_table, *table_to_update; TMP_TABLE_PARAM *tmp_table_param; @@ -5632,6 +5633,7 @@ public: List *fields, List *values, enum_duplicates handle_duplicates, bool ignore); ~multi_update(); + bool init(THD *thd); int prepare(List &list, SELECT_LEX_UNIT *u); int send_data(List &items); bool initialize_tables (JOIN *join); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e2d4cd47580..61a3b4e8e75 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -841,9 +841,6 @@ int mysql_multi_delete_prepare(THD *thd) DELETE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - /* Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it @@ -871,6 +868,12 @@ int mysql_multi_delete_prepare(THD *thd) target_tbl->table_name, "DELETE"); DBUG_RETURN(TRUE); } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { /* Check that table from which we delete is not used somewhere inside subqueries/view. @@ -915,12 +918,6 @@ multi_delete::prepare(List &values, SELECT_LEX_UNIT *u) unit= u; do_delete= 1; THD_STAGE_INFO(thd, stage_deleting_from_main_table); - SELECT_LEX *select_lex= u->first_select(); - if (select_lex->first_cond_optimization) - { - if (select_lex->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - } DBUG_RETURN(0); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 5a85b7ea7e3..5f90f2f9ab0 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -354,10 +354,6 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_RETURN(FALSE); } - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || - thd->lex->sql_command == SQLCOM_DELETE_MULTI) - thd->save_prep_leaf_list= TRUE; - arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test if (!derived->merged_for_insert || @@ -435,6 +431,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) derived->on_expr= expr; derived->prep_on_expr= expr->copy_andor_structure(thd); } + thd->where= "on clause"; if (derived->on_expr && ((!derived->on_expr->fixed && derived->on_expr->fix_fields(thd, &derived->on_expr)) || diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index bfe773b2c00..57c6dfad4e5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4623,6 +4623,27 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) } +/** + Set exclude_from_table_unique_test for selects of this select and all selects + belonging to the underlying units of derived tables or views +*/ + +void st_select_lex::set_unique_exclude() +{ + exclude_from_table_unique_test= TRUE; + for (SELECT_LEX_UNIT *unit= first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (unit->derived && unit->derived->is_view_or_derived()) + { + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + sl->set_unique_exclude(); + } + } +} + + /* Return true if this select_lex has been converted into a semi-join nest within 'ancestor'. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 31751a16471..49265accc07 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1147,6 +1147,8 @@ public: bool save_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd); + void set_unique_exclude(); + bool is_merged_child_of(st_select_lex *ancestor); /* diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 01743a6751e..e8b973c03bd 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1390,15 +1390,8 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) call in setup_tables()). */ - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(1); - - if (select_lex->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(1); - - if (thd->lex->save_prep_leaf_tables()) + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, TRUE)) DBUG_RETURN(1); List *fields= &lex->select_lex.item_list; @@ -1574,7 +1567,8 @@ int mysql_multi_update_prepare(THD *thd) Check that we are not using table that we are updating, but we should skip all tables of UPDATE SELECT itself */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->select_lex.set_unique_exclude(); + /* We only need SELECT privilege for columns in the values list */ List_iterator ti(lex->select_lex.leaf_tables); while ((tl= ti++)) @@ -1635,9 +1629,16 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List *fields, DBUG_RETURN(TRUE); } + if ((*result)->init(thd)) + DBUG_RETURN(1); + thd->abort_on_warning= !ignore && thd->is_strict_mode(); List total_list; + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, FALSE)) + DBUG_RETURN(1); + res= mysql_select(thd, table_list, select_lex->with_wild, total_list, conds, 0, NULL, NULL, NULL, NULL, @@ -1673,6 +1674,24 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, {} +bool multi_update::init(THD *thd) +{ + table_map tables_to_update= get_table_map(fields); + List_iterator_fast li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (tbl->is_jtbm()) + continue; + if (!(tbl->table->map & tables_to_update)) + continue; + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -1689,7 +1708,7 @@ int multi_update::prepare(List ¬_used_values, List_iterator_fast value_it(*values); uint i, max_fields; uint leaf_table_count= 0; - List_iterator ti(*leaves); + List_iterator ti(updated_leaves); DBUG_ENTER("multi_update::prepare"); if (prepared) From e3a25793be936d9682a711a00d6b4bf708b6fb8d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 22 Apr 2021 20:02:08 -0700 Subject: [PATCH 07/14] MDEV-24823 Crash with invalid multi-table update of view in 2nd execution of SP Before this patch mergeable derived tables / view used in a multi-table update / delete were merged before the preparation stage. When the merge of a derived table / view is performed the on expression attached to it is fixed and ANDed with the where condition of the select S containing this derived table / view. It happens after the specification of the derived table / view has been merged into S. If the ON expression refers to a non existing field an error is reported and some other mergeable derived tables / views remain unmerged. It's not a problem if the multi-table update / delete statement is standalone. Yet if it is used in a stored procedure the select with incompletely merged derived tables / views may cause a problem for the second call of the procedure. This does not happen for select queries using derived tables / views, because in this case their specifications are merged after the preparation stage at which all ON expressions are fixed. This patch makes sure that merging of the derived tables / views used in a multi-table update / delete statement is performed after the preparation stage. Approved by Oleksandr Byelkin --- mysql-test/main/multi_update.result | 53 +++++++++++++++++++++++++++++ mysql-test/main/multi_update.test | 53 +++++++++++++++++++++++++++++ sql/sql_base.cc | 3 +- sql/sql_class.h | 4 ++- sql/sql_delete.cc | 15 ++++---- sql/sql_derived.cc | 5 +-- sql/sql_update.cc | 39 +++++++++++++++------ 7 files changed, 147 insertions(+), 25 deletions(-) diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result index b3edeb77c44..520199d562c 100644 --- a/mysql-test/main/multi_update.result +++ b/mysql-test/main/multi_update.result @@ -982,6 +982,59 @@ drop function f1; # # end of 5.5 tests # +# +# MDEV-24823: Invalid multi-table update of view within SP +# +create table t1 (id int) engine=myisam; +insert into t1 values (1),(2),(1); +create table t2 (pk int, c0 int) engine=myisam; +insert into t2 values (1,1), (2,3); +create view v2 as select * from t2; +create view v3 as select * from t2 where c0 < 3; +create procedure sp0() update t1, v2 set v2.pk = 1 where v2.c0 = t1.c1; +call sp0(); +ERROR 42S22: Unknown column 't1.c1' in 'where clause' +call sp0(); +ERROR 42S22: Unknown column 't1.c1' in 'where clause' +create procedure sp1() update (t1 join v2 on v2.c0 = t1.c1) set v2.pk = 1; +call sp1(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp1(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +create procedure sp2() update (t1 join v3 on v3.c0 = t1.c1) set v3.pk = 1; +call sp2(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp2(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +create procedure sp3() +update (t1 join v2 on v2.c0 = t1.id) set v2.c0 = v2.c0+1; +select * from t2; +pk c0 +1 1 +2 3 +call sp3(); +select * from t2; +pk c0 +1 2 +2 3 +call sp3(); +select * from t2; +pk c0 +1 3 +2 3 +create procedure sp4() delete t1 from t1 join v2 on v2.c0 = t1.c1; +call sp4(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +call sp4(); +ERROR 42S22: Unknown column 't1.c1' in 'on clause' +drop procedure sp0; +drop procedure sp1; +drop procedure sp2; +drop procedure sp3; +drop procedure sp4; +drop view v2,v3; +drop table t1,t2; +# End of 10.2 tests create table t1 (c1 int, c3 int); insert t1(c3) values (1), (2), (3), (4), (5), (6), (7), (8); create table t2 select * from t1; diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test index 8a32f626818..84f06a7c165 100644 --- a/mysql-test/main/multi_update.test +++ b/mysql-test/main/multi_update.test @@ -958,6 +958,59 @@ drop function f1; --echo # end of 5.5 tests --echo # +--echo # +--echo # MDEV-24823: Invalid multi-table update of view within SP +--echo # + +create table t1 (id int) engine=myisam; +insert into t1 values (1),(2),(1); +create table t2 (pk int, c0 int) engine=myisam; +insert into t2 values (1,1), (2,3); +create view v2 as select * from t2; +create view v3 as select * from t2 where c0 < 3; + +create procedure sp0() update t1, v2 set v2.pk = 1 where v2.c0 = t1.c1; +--error ER_BAD_FIELD_ERROR +call sp0(); +--error ER_BAD_FIELD_ERROR +call sp0(); + +create procedure sp1() update (t1 join v2 on v2.c0 = t1.c1) set v2.pk = 1; +--error ER_BAD_FIELD_ERROR +call sp1(); +--error ER_BAD_FIELD_ERROR +call sp1(); + +create procedure sp2() update (t1 join v3 on v3.c0 = t1.c1) set v3.pk = 1; +--error ER_BAD_FIELD_ERROR +call sp2(); +--error ER_BAD_FIELD_ERROR +call sp2(); + +create procedure sp3() +update (t1 join v2 on v2.c0 = t1.id) set v2.c0 = v2.c0+1; +select * from t2; +call sp3(); +select * from t2; +call sp3(); +select * from t2; + +create procedure sp4() delete t1 from t1 join v2 on v2.c0 = t1.c1; +--error ER_BAD_FIELD_ERROR +call sp4(); +--error ER_BAD_FIELD_ERROR +call sp4(); + +drop procedure sp0; +drop procedure sp1; +drop procedure sp2; +drop procedure sp3; +drop procedure sp4; +drop view v2,v3; +drop table t1,t2; + +--echo # End of 10.2 tests + # # MDEV-13911 Support ORDER BY and LIMIT in multi-table update # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0b17032da8f..74ac1a3dbdb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7780,7 +7780,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (table_list->jtbm_subselect) { Item *item= table_list->jtbm_subselect->optimizer; - if (table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) + if (!table_list->jtbm_subselect->optimizer->fixed && + table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) { my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast(MAX_TABLES)); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/ DBUG_RETURN(1); diff --git a/sql/sql_class.h b/sql/sql_class.h index 5ab93de7957..4f0df744b92 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6117,7 +6117,8 @@ public: class multi_update :public select_result_interceptor { TABLE_LIST *all_tables; /* query/update command tables */ - List *leaves; /* list of leves of join table tree */ + List *leaves; /* list of leaves of join table tree */ + List updated_leaves; /* list of of updated leaves */ TABLE_LIST *update_tables; TABLE **tmp_tables, *main_table, *table_to_update; TMP_TABLE_PARAM *tmp_table_param; @@ -6155,6 +6156,7 @@ public: List *fields, List *values, enum_duplicates handle_duplicates, bool ignore); ~multi_update(); + bool init(THD *thd); int prepare(List &list, SELECT_LEX_UNIT *u); int send_data(List &items); bool initialize_tables (JOIN *join); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0857c4e5457..c78e8ed510e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1009,9 +1009,6 @@ int mysql_multi_delete_prepare(THD *thd) DELETE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - /* Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it @@ -1039,6 +1036,12 @@ int mysql_multi_delete_prepare(THD *thd) target_tbl->table_name.str, "DELETE"); DBUG_RETURN(TRUE); } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { /* Check that table from which we delete is not used somewhere inside subqueries/view. @@ -1083,12 +1086,6 @@ multi_delete::prepare(List &values, SELECT_LEX_UNIT *u) unit= u; do_delete= 1; THD_STAGE_INFO(thd, stage_deleting_from_main_table); - SELECT_LEX *select_lex= u->first_select(); - if (select_lex->first_cond_optimization) - { - if (select_lex->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - } DBUG_RETURN(0); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 50b0178c6c9..eef2ae7d282 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -355,10 +355,6 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_RETURN(FALSE); } - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || - thd->lex->sql_command == SQLCOM_DELETE_MULTI) - thd->save_prep_leaf_list= TRUE; - arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test if (!derived->merged_for_insert || @@ -436,6 +432,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) derived->on_expr= expr; derived->prep_on_expr= expr->copy_andor_structure(thd); } + thd->where= "on clause"; if (derived->on_expr && derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr)) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index c66a474c18b..c58eb4e193d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1571,15 +1571,8 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) call in setup_tables()). */ - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(1); - - if (select_lex->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(1); - - if (thd->lex->save_prep_leaf_tables()) + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, TRUE)) DBUG_RETURN(1); List *fields= &lex->select_lex.item_list; @@ -1755,6 +1748,7 @@ int mysql_multi_update_prepare(THD *thd) skip all tables of UPDATE SELECT itself */ lex->select_lex.exclude_from_table_unique_test= TRUE; + /* We only need SELECT privilege for columns in the values list */ List_iterator ti(lex->select_lex.leaf_tables); while ((tl= ti++)) @@ -1805,9 +1799,16 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List *fields, DBUG_RETURN(TRUE); } + if ((*result)->init(thd)) + DBUG_RETURN(1); + thd->abort_on_warning= !ignore && thd->is_strict_mode(); List total_list; + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, FALSE)) + DBUG_RETURN(1); + if (select_lex->vers_setup_conds(thd, table_list)) DBUG_RETURN(1); @@ -1849,6 +1850,24 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, } +bool multi_update::init(THD *thd) +{ + table_map tables_to_update= get_table_map(fields); + List_iterator_fast li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (tbl->is_jtbm()) + continue; + if (!(tbl->table->map & tables_to_update)) + continue; + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -1865,7 +1884,7 @@ int multi_update::prepare(List ¬_used_values, List_iterator_fast value_it(*values); uint i, max_fields; uint leaf_table_count= 0; - List_iterator ti(*leaves); + List_iterator ti(updated_leaves); DBUG_ENTER("multi_update::prepare"); if (prepared) From 018d7440fdcf1baf3d0ce235092433d4ea46c485 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 22 Apr 2021 11:18:30 +0200 Subject: [PATCH 08/14] remove EXCEPTIONS-CLIENT It's Oracle libmysqlclient license exception, we no longer include, build or ship libmysqlclient --- CMakeLists.txt | 3 +- EXCEPTIONS-CLIENT | 136 ---------------------------------------------- 2 files changed, 1 insertion(+), 138 deletions(-) delete mode 100644 EXCEPTIONS-CLIENT diff --git a/CMakeLists.txt b/CMakeLists.txt index 36662113108..3decb4bac67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -485,8 +485,7 @@ ADD_CUSTOM_TARGET(INFO_BIN ALL WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) -INSTALL_DOCUMENTATION(README.md CREDITS COPYING THIRDPARTY - EXCEPTIONS-CLIENT COMPONENT Readme) +INSTALL_DOCUMENTATION(README.md CREDITS COPYING THIRDPARTY COMPONENT Readme) # MDEV-6526 these files are not installed anymore #INSTALL_DOCUMENTATION(${CMAKE_BINARY_DIR}/Docs/INFO_SRC diff --git a/EXCEPTIONS-CLIENT b/EXCEPTIONS-CLIENT deleted file mode 100644 index 11ba42c422f..00000000000 --- a/EXCEPTIONS-CLIENT +++ /dev/null @@ -1,136 +0,0 @@ - FOSS License Exception - - http://www.mysql.com/about/legal/licensing/foss-exception/ - Updated February 23, 2012 - -What is the FOSS License Exception? - - Oracle's Free and Open Source Software ("FOSS") License Exception - (formerly known as the FLOSS License Exception) allows developers of FOSS - applications to include Oracle's MySQL Client Libraries (also referred to - as "MySQL Drivers" or "MySQL Connectors") with their FOSS applications. - MySQL Client Libraries are typically licensed pursuant to version 2 of the - General Public License ("GPL"), but this exception permits distribution of - certain MySQL Client Libraries with a developer's FOSS applications - licensed under the terms of another FOSS license listed below, even though - such other FOSS license may be incompatible with the GPL. - - The following terms and conditions describe the circumstances under which - Oracle's FOSS License Exception applies. - -Oracle's FOSS License Exception Terms and Conditions - - 1. Definitions. "Derivative Work" means a derivative work, as defined - under applicable copyright law, formed entirely from the Program and - one or more FOSS Applications. - - "FOSS Application" means a free and open source software application - distributed subject to a license listed in the section below titled - "FOSS License List." - - "FOSS Notice" means a notice placed by Oracle or MySQL in a copy of - the MySQL Client Libraries stating that such copy of the MySQL Client - Libraries may be distributed under Oracle's or MySQL's FOSS (or FLOSS) - License Exception. - - "Independent Work" means portions of the Derivative Work that are not - derived from the Program and can reasonably be considered independent - and separate works. - - "Program" means a copy of Oracle's MySQL Client Libraries that - contains a FOSS Notice. - - 2. A FOSS application developer ("you" or "your") may distribute a - Derivative Work provided that you and the Derivative Work meet all of - the following conditions: - - a. You obey the GPL in all respects for the Program and all portions - (including modifications) of the Program included in the - Derivative Work (provided that this condition does not apply to - Independent Works); - b. The Derivative Work does not include any work licensed under the - GPL other than the Program; - c. You distribute Independent Works subject to a license listed in - the section below titled "FOSS License List"; - d. You distribute Independent Works in object code or executable - form with the complete corresponding machine-readable source code - on the same medium and under the same FOSS license applying to - the object code or executable forms; - e. All works that are aggregated with the Program or the Derivative - Work on a medium or volume of storage are not derivative works of - the Program, Derivative Work or FOSS Application, and must - reasonably be considered independent and separate works. - - 3. Oracle reserves all rights not expressly granted in these terms and - conditions. If all of the above conditions are not met, then this FOSS - License Exception does not apply to you or your Derivative Work. - -FOSS License List - -+------------------------------------------------------------------------+ -|License Name |Version(s)/Copyright Date| -|----------------------------------------------+-------------------------| -|Academic Free License |2.0 | -|----------------------------------------------+-------------------------| -|Apache Software License |1.0/1.1/2.0 | -|----------------------------------------------+-------------------------| -|Apple Public Source License |2.0 | -|----------------------------------------------+-------------------------| -|Artistic license |From Perl 5.8.0 | -|----------------------------------------------+-------------------------| -|BSD license |"July 22 1999" | -|----------------------------------------------+-------------------------| -|Common Development and Distribution License |1.0 | -|(CDDL) | | -|----------------------------------------------+-------------------------| -|Common Public License |1.0 | -|----------------------------------------------+-------------------------| -|Eclipse Public License |1.0 | -|----------------------------------------------+-------------------------| -|European Union Public License (EUPL)¹ |1.1 | -|----------------------------------------------+-------------------------| -|GNU Affero General Public License (AGPL) |3.0 | -|----------------------------------------------+-------------------------| -|GNU Library or "Lesser" General Public License|2.0/2.1/3.0 | -|(LGPL) | | -|----------------------------------------------+-------------------------| -|GNU General Public License (GPL) |3.0 | -|----------------------------------------------+-------------------------| -|IBM Public License |1.0 | -|----------------------------------------------+-------------------------| -|Jabber Open Source License |1.0 | -|----------------------------------------------+-------------------------| -|MIT License (As listed in file |- | -|MIT-License.txt) | | -|----------------------------------------------+-------------------------| -|Mozilla Public License (MPL) |1.0/1.1 | -|----------------------------------------------+-------------------------| -|Open Software License |2.0 | -|----------------------------------------------+-------------------------| -|OpenSSL license (with original SSLeay license)|"2003" ("1998") | -|----------------------------------------------+-------------------------| -|PHP License |3.0/3.01 | -|----------------------------------------------+-------------------------| -|Python license (CNRI Python License) |- | -|----------------------------------------------+-------------------------| -|Python Software Foundation License |2.1.1 | -|----------------------------------------------+-------------------------| -|Sleepycat License |"1999" | -|----------------------------------------------+-------------------------| -|University of Illinois/NCSA Open Source |- | -|License | | -|----------------------------------------------+-------------------------| -|W3C License |"2001" | -|----------------------------------------------+-------------------------| -|X11 License |"2001" | -|----------------------------------------------+-------------------------| -|Zlib/libpng License |- | -|----------------------------------------------+-------------------------| -|Zope Public License |2.0 | -+------------------------------------------------------------------------+ - -¹) When an Independent Work is licensed under a "Compatible License" -pursuant to the EUPL, the Compatible License rather than the EUPL is the -applicable license for purposes of these FOSS License Exception Terms and -Conditions. - From 4649ba7493897b3a140ab354b88decd3f0540491 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 25 Mar 2021 11:33:11 +0300 Subject: [PATCH 09/14] MDEV-23455 Hangs + Sig11 in unknown location(s) due to single complex FK query Buffer overflow in ib_push_warning() fixed by using vsnprintf(). InnoDB parser was obsoleted by MDEV-16417. Thanks to Nikita Malyavin for review and suggestion. --- mysql-test/suite/innodb/r/foreign_key.result | 11 ++++++ mysql-test/suite/innodb/t/foreign_key.test | 39 ++++++++++++++++++++ storage/innobase/handler/ha_innodb.cc | 7 ++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index ca27595ffa0..efaa9028eae 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -801,4 +801,15 @@ ERROR 23000: Duplicate entry '10' for key 'ind9' SET FOREIGN_KEY_CHECKS= 0; ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (pk); DROP TABLE t1; +SET FOREIGN_KEY_CHECKS= 1; +# +# MDEV-23455 Hangs + Sig11 in unknown location(s) due to single complex FK query +# +Parsing foreign keys 1... +ERROR HY000: Can't create table `test`.`t0` (errno: 150 "Foreign key constraint is incorrectly formed") +Parsing foreign keys 2... +ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed") +Parsing foreign keys 3... +ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed") +Parsing foreign keys 4... # End of 10.2 tests diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index 7756d48046f..08f2c529659 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -782,6 +782,45 @@ ALTER TABLE t1 ADD UNIQUE INDEX ind9 (b), LOCK=SHARED; SET FOREIGN_KEY_CHECKS= 0; ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (pk); DROP TABLE t1; +SET FOREIGN_KEY_CHECKS= 1; + +--echo # +--echo # MDEV-23455 Hangs + Sig11 in unknown location(s) due to single complex FK query +--echo # +let $constr_prefix= aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; +let $fk_ref= xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; +let $fk_field= yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy; +let $constr_count= 200; # each 100 constrs is 1 sec of test execution +let $i= 0; + +while ($i < $constr_count) +{ + let $p= $constr_prefix$i; + let $constr= CONSTRAINT $p FOREIGN KEY ($fk_field) REFERENCES t1($fk_ref) ON UPDATE SET NULL; + if ($i) + { + let $constrs= $constrs, $constr; + } + if (!$i) + { + let $constrs= $constr; + } + inc $i; +} +--disable_query_log +--echo Parsing foreign keys 1... +--error ER_CANT_CREATE_TABLE +eval create table t0($fk_field int, $constrs) engine innodb; +--echo Parsing foreign keys 2... +--error ER_CANT_CREATE_TABLE +eval create table t1($fk_field int, $constrs) engine innodb; +--echo Parsing foreign keys 3... +--error ER_CANT_CREATE_TABLE +eval create table t1($fk_ref int, $fk_field int, $constrs) engine innodb; +--echo Parsing foreign keys 4... +eval create table t1($fk_ref int primary key, $fk_field int, $constrs) engine innodb; +drop table t1; +--enable_query_log --echo # End of 10.2 tests diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index c95e6646968..a725569fa6e 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -22600,8 +22600,8 @@ ib_push_warning( va_start(args, format); buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); - vsprintf(buf,format, args); - + buf[MAX_BUF_SIZE - 1] = 0; + vsnprintf(buf, MAX_BUF_SIZE - 1, format, args); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, convert_error_code_to_mysql((dberr_t)error, 0, thd), buf); @@ -22632,7 +22632,8 @@ ib_push_warning( if (thd) { va_start(args, format); buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); - vsprintf(buf,format, args); + buf[MAX_BUF_SIZE - 1] = 0; + vsnprintf(buf, MAX_BUF_SIZE - 1, format, args); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, convert_error_code_to_mysql((dberr_t)error, 0, thd), From 42f8548ff6aed90439c94a388399e54afc871886 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 26 Mar 2021 11:44:54 +0300 Subject: [PATCH 10/14] MDEV-25091 CREATE TABLE: field references qualified by a wrong table name succeed Before FRM is written walk vcol expressions through check_table_name_processor() and check if field items match (db, table_name) qualifier. We cannot do this in check_vcol_func_processor() as there is already no table name qualifiers in expressions of written and loaded FRM. --- mysql-test/suite/vcol/r/vcol_syntax.result | 38 ++++++++++++++ mysql-test/suite/vcol/t/vcol_syntax.test | 44 ++++++++++++++++ sql/item.h | 36 ++++++++++++++ sql/sql_lex.h | 17 +++++++ sql/sql_table.cc | 58 ++++++++++++++++++++-- 5 files changed, 188 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_syntax.result b/mysql-test/suite/vcol/r/vcol_syntax.result index 16e30e57230..c8983f34c93 100644 --- a/mysql-test/suite/vcol/r/vcol_syntax.result +++ b/mysql-test/suite/vcol/r/vcol_syntax.result @@ -50,3 +50,41 @@ t1 CREATE TABLE "t1" ( ) drop table t1; set session sql_mode=@OLD_SQL_MODE; +# +# MDEV-25091 CREATE TABLE: field references qualified by a wrong table name succeed +# +create table t2 (x int); +create table t1 (x int, y int generated always as (t2.x)); +ERROR 42S22: Unknown column '`t2`.`x`' in 'GENERATED ALWAYS' +create table t1 (x int, y int check (y > t2.x)); +ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' +create table t1 (x int, y int default t2.x); +ERROR 42S22: Unknown column '`t2`.`x`' in 'DEFAULT' +create table t1 (x int, check (t2.x > 0)); +ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' +create table t1 (x int); +alter table t1 add column y int generated always as (t2.x); +ERROR 42S22: Unknown column '`t2`.`x`' in 'GENERATED ALWAYS' +alter table t1 add column y int check (z > t2.x); +ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' +alter table t1 add column y int default t2.x; +ERROR 42S22: Unknown column '`t2`.`x`' in 'DEFAULT' +alter table t1 add constraint check (t2.x > 0); +ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' +create or replace table t1 (x int, y int generated always as (t1.x)); +create or replace table t1 (x int, y int check (y > t1.x)); +create or replace table t1 (x int, y int default t1.x); +create or replace table t1 (x int, check (t1.x > 0)); +create or replace table t1 (x int, y int generated always as (test.t1.x)); +create or replace table t1 (x int, y int check (y > test.t1.x)); +create or replace table t1 (x int, y int default test.t1.x); +create or replace table t1 (x int, check (test.t1.x > 0)); +drop tables t1, t2; +create table t1 (x int, y int generated always as (test2.t1.x)); +ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'GENERATED ALWAYS' +create table t1 (x int, y int check (y > test2.t1.x)); +ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'CHECK' +create table t1 (x int, y int default test2.t1.x); +ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'DEFAULT' +create table t1 (x int, check (test2.t1.x > 0)); +ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'CHECK' diff --git a/mysql-test/suite/vcol/t/vcol_syntax.test b/mysql-test/suite/vcol/t/vcol_syntax.test index 6dc3cf43317..f425b52ab79 100644 --- a/mysql-test/suite/vcol/t/vcol_syntax.test +++ b/mysql-test/suite/vcol/t/vcol_syntax.test @@ -28,3 +28,47 @@ show create table t1; drop table t1; set session sql_mode=@OLD_SQL_MODE; +--echo # +--echo # MDEV-25091 CREATE TABLE: field references qualified by a wrong table name succeed +--echo # +create table t2 (x int); + +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int generated always as (t2.x)); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int check (y > t2.x)); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int default t2.x); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, check (t2.x > 0)); + +create table t1 (x int); +--error ER_BAD_FIELD_ERROR +alter table t1 add column y int generated always as (t2.x); +--error ER_BAD_FIELD_ERROR +alter table t1 add column y int check (z > t2.x); +--error ER_BAD_FIELD_ERROR +alter table t1 add column y int default t2.x; +--error ER_BAD_FIELD_ERROR +alter table t1 add constraint check (t2.x > 0); + +create or replace table t1 (x int, y int generated always as (t1.x)); +create or replace table t1 (x int, y int check (y > t1.x)); +create or replace table t1 (x int, y int default t1.x); +create or replace table t1 (x int, check (t1.x > 0)); + +create or replace table t1 (x int, y int generated always as (test.t1.x)); +create or replace table t1 (x int, y int check (y > test.t1.x)); +create or replace table t1 (x int, y int default test.t1.x); +create or replace table t1 (x int, check (test.t1.x > 0)); + +drop tables t1, t2; + +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int generated always as (test2.t1.x)); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int check (y > test2.t1.x)); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, y int default test2.t1.x); +--error ER_BAD_FIELD_ERROR +create table t1 (x int, check (test2.t1.x > 0)); diff --git a/sql/item.h b/sql/item.h index b13336939f9..c94709c733e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1719,6 +1719,15 @@ public: return 0; } + /** + Check db/table_name if they defined in item and match arg values + + @param arg Pointer to Check_table_name_prm structure + + @retval true Match failed + @retval false Match succeeded + */ + virtual bool check_table_name_processor(void *arg) { return false; } /* TRUE if the expression depends only on the table indicated by tab_map or can be converted to such an exression using equalities. @@ -1850,6 +1859,15 @@ public: bool collect; }; + struct Check_table_name_prm + { + LEX_CSTRING db; + LEX_CSTRING table_name; + String field; + Check_table_name_prm(LEX_CSTRING _db, LEX_CSTRING _table_name) : + db(_db), table_name(_table_name) {} + }; + /* For SP local variable returns pointer to Item representing its current value and pointer to current Item otherwise. @@ -2820,6 +2838,24 @@ public: } return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF); } + bool check_table_name_processor(void *arg) + { + Check_table_name_prm &p= *(Check_table_name_prm *) arg; + if (p.table_name.length && table_name) + { + DBUG_ASSERT(p.db.length); + if ((db_name && + my_strcasecmp(table_alias_charset, p.db.str, db_name)) || + my_strcasecmp(table_alias_charset, p.table_name.str, table_name)) + { + print(&p.field, (enum_query_type) (QT_ITEM_ORIGINAL_FUNC_NULLIF | + QT_NO_DATA_EXPANSION | + QT_TO_SYSTEM_CHARSET)); + return true; + } + } + return false; + } void cleanup(); Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 49265accc07..0dab0f0666d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3207,6 +3207,23 @@ public: } return false; } + + bool create_like() const + { + DBUG_ASSERT(!create_info.like() || !select_lex.item_list.elements); + return create_info.like(); + } + + bool create_select() const + { + DBUG_ASSERT(!create_info.like() || !select_lex.item_list.elements); + return select_lex.item_list.elements; + } + + bool create_simple() const + { + return !create_like() && !create_select(); + } }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d472e2332f2..07762e64259 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -76,7 +76,9 @@ static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, static bool prepare_blob_field(THD *thd, Column_definition *sql_field); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, - uint *, handler *, KEY **, uint *, int); + uint *, handler *, KEY **, uint *, int, + const LEX_CSTRING db, + const LEX_CSTRING table_name); static uint blob_length_by_type(enum_field_types type); static bool fix_constraints_names(THD *thd, List *check_constraint_list); @@ -1809,10 +1811,12 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); if (flags & WFRM_WRITE_SHADOW) { + LEX_CSTRING db= { lpt->db, strlen(lpt->db) }; + LEX_CSTRING table_name= { lpt->table_name, strlen(lpt->table_name) }; if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, &lpt->db_options, lpt->table->file, &lpt->key_info_buffer, &lpt->key_count, - C_ALTER_TABLE)) + C_ALTER_TABLE, db, table_name)) { DBUG_RETURN(TRUE); } @@ -3231,7 +3235,8 @@ static int mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Alter_info *alter_info, uint *db_options, handler *file, KEY **key_info_buffer, - uint *key_count, int create_table_mode) + uint *key_count, int create_table_mode, + const LEX_CSTRING db, const LEX_CSTRING table_name) { const char *key_name; Create_field *sql_field,*dup_field; @@ -3246,6 +3251,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, uint total_uneven_bit_length= 0; int select_field_count= C_CREATE_SELECT(create_table_mode); bool tmp_table= create_table_mode == C_ALTER_TABLE; + const bool create_simple= thd->lex->create_simple(); DBUG_ENTER("mysql_prepare_create_table"); LEX_STRING* connect_string = &create_info->connect_string; @@ -4138,6 +4144,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, create_info->null_bits= null_fields; /* Check fields. */ + Item::Check_table_name_prm walk_prm(db, table_name); it.rewind(); while ((sql_field=it++)) { @@ -4182,6 +4189,37 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); DBUG_RETURN(TRUE); } + + if (create_simple) + { + /* + NOTE: we cannot do this in check_vcol_func_processor() as there is already + no table name qualifier in expression. + */ + if (sql_field->vcol_info && + sql_field->vcol_info->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "GENERATED ALWAYS"); + DBUG_RETURN(TRUE); + } + + if (sql_field->default_value && + sql_field->default_value->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "DEFAULT"); + DBUG_RETURN(TRUE); + } + + if (sql_field->check_constraint && + sql_field->check_constraint->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK"); + DBUG_RETURN(TRUE); + } + } } /* Check table level constraints */ @@ -4191,6 +4229,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Virtual_column_info *check; while ((check= c_it++)) { + if (create_simple && check->expr->walk(&Item::check_table_name_processor, false, + (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK"); + DBUG_RETURN(TRUE); + } if (!check->name.length || check->automatic_name) continue; @@ -4477,6 +4521,8 @@ handler *mysql_create_frm_image(THD *thd, { uint db_options; handler *file; + const LEX_CSTRING new_db= { db, strlen(db) }; + const LEX_CSTRING new_table_name= { table_name, strlen(table_name) }; DBUG_ENTER("mysql_create_frm_image"); if (!alter_info->create_list.elements) @@ -4700,7 +4746,7 @@ handler *mysql_create_frm_image(THD *thd, if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, file, key_info, key_count, - create_table_mode)) + create_table_mode, new_db, new_table_name)) goto err; create_info->table_options=db_options; @@ -7024,13 +7070,15 @@ bool mysql_compare_tables(TABLE *table, Alter_info tmp_alter_info(*alter_info, thd->mem_root); uint db_options= 0; /* not used */ KEY *key_info_buffer= NULL; + LEX_CSTRING db= { table->s->db.str, table->s->db.length }; + LEX_CSTRING table_name= { table->s->db.str, table->s->table_name.length }; /* Create the prepared information. */ int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ? C_ORDINARY_CREATE : C_ALTER_TABLE; if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, &db_options, table->file, &key_info_buffer, - &key_count, create_table_mode)) + &key_count, create_table_mode, db, table_name)) DBUG_RETURN(1); /* Some very basic checks. */ From 1288dfffe77a99d6c5906d12010a1677ee149308 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 23 Apr 2021 14:32:20 -0700 Subject: [PATCH 11/14] This patch complements the patch for MDEV-24823. --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 61a3b4e8e75..e2bf2d727b1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -845,7 +845,7 @@ int mysql_multi_delete_prepare(THD *thd) Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->select_lex.set_unique_exclude(); /* Fix tables-to-be-deleted-from list to point at opened tables */ for (target_tbl= (TABLE_LIST*) aux_tables; target_tbl; From 25ed665a2047e4766bc380ab30977a337f42485e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 23 Apr 2021 14:49:32 +0300 Subject: [PATCH 12/14] MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows row_sel_sec_rec_is_for_clust_rec(): If the field in the clustered index record stored off page, always fetch it, also when the secondary index field has been built on the entire column. This was broken ever since the InnoDB Plugin for MySQL Server 5.1 introduced ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPRESSED for InnoDB tables. That code was first introduced in this tree in commit 3945d5e5549187a18c64a112899f90a7f6a320d6. For the original ROW_FORMAT=REDUNDANT and the MySQL 5.0.3 ROW_FORMAT=COMPRESSED, there was no problem, because for those tables we always stored at least a 768-byte prefix of each column in the clustered index record. row_sel_sec_rec_is_for_blob(): Allow prefix_len==0 for matching the full column. --- .../suite/innodb/r/mvcc_secondary.result | 24 +++++ mysql-test/suite/innodb/t/mvcc_secondary.test | 26 ++++++ storage/innobase/row/row0sel.cc | 90 +++++++++++-------- 3 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 mysql-test/suite/innodb/r/mvcc_secondary.result create mode 100644 mysql-test/suite/innodb/t/mvcc_secondary.test diff --git a/mysql-test/suite/innodb/r/mvcc_secondary.result b/mysql-test/suite/innodb/r/mvcc_secondary.result new file mode 100644 index 00000000000..2289742e830 --- /dev/null +++ b/mysql-test/suite/innodb/r/mvcc_secondary.result @@ -0,0 +1,24 @@ +# +# MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows +# +CREATE TABLE t1 ( +pk int PRIMARY KEY, c varchar(255) UNIQUE, +d char(255), e varchar(255), f char(255), g char(255) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARACTER SET ucs2; +INSERT INTO t1 VALUES +(1,REPEAT('c',248),REPEAT('a',106),REPEAT('b',220),REPEAT('x',14),''); +BEGIN; +UPDATE t1 SET c=REPEAT('d',170); +connect con1,localhost,root,,; +SELECT pk FROM t1 FORCE INDEX (c); +pk +1 +connection default; +COMMIT; +connection con1; +SELECT pk FROM t1 FORCE INDEX (c); +pk +1 +disconnect con1; +connection default; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/mvcc_secondary.test b/mysql-test/suite/innodb/t/mvcc_secondary.test new file mode 100644 index 00000000000..93c91c40076 --- /dev/null +++ b/mysql-test/suite/innodb/t/mvcc_secondary.test @@ -0,0 +1,26 @@ +--source include/innodb_page_size_small.inc + +--echo # +--echo # MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows +--echo # + +CREATE TABLE t1 ( + pk int PRIMARY KEY, c varchar(255) UNIQUE, + d char(255), e varchar(255), f char(255), g char(255) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARACTER SET ucs2; + +INSERT INTO t1 VALUES +(1,REPEAT('c',248),REPEAT('a',106),REPEAT('b',220),REPEAT('x',14),''); + +BEGIN; +UPDATE t1 SET c=REPEAT('d',170); + +connect (con1,localhost,root,,); +SELECT pk FROM t1 FORCE INDEX (c); +connection default; +COMMIT; +connection con1; +SELECT pk FROM t1 FORCE INDEX (c); +disconnect con1; +connection default; +DROP TABLE t1; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 864f7bd54ab..86cb6b0ea1c 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -79,9 +79,9 @@ is alphabetically the same as the corresponding BLOB column in the clustered index record. NOTE: the comparison is NOT done as a binary comparison, but character fields are compared with collation! -@return TRUE if the columns are equal */ +@return whether the columns are equal */ static -ibool +bool row_sel_sec_rec_is_for_blob( /*========================*/ ulint mtype, /*!< in: main type */ @@ -100,19 +100,18 @@ row_sel_sec_rec_is_for_blob( const byte* sec_field, /*!< in: column in secondary index */ ulint sec_len, /*!< in: length of sec_field */ ulint prefix_len, /*!< in: index column prefix length - in bytes */ + in bytes, or 0 for full column */ dict_table_t* table) /*!< in: table */ { ulint len; - byte buf[REC_VERSION_56_MAX_INDEX_COL_LEN]; + byte buf[REC_VERSION_56_MAX_INDEX_COL_LEN + 1]; /* This function should never be invoked on an Antelope format table, because they should always contain enough prefix in the clustered index record. */ ut_ad(dict_table_get_format(table) >= UNIV_FORMAT_B); ut_a(clust_len >= BTR_EXTERN_FIELD_REF_SIZE); - ut_ad(prefix_len >= sec_len); - ut_ad(prefix_len > 0); + ut_ad(!prefix_len || prefix_len >= sec_len); ut_a(prefix_len <= sizeof buf); if (!memcmp(clust_field + clust_len - BTR_EXTERN_FIELD_REF_SIZE, @@ -121,11 +120,12 @@ row_sel_sec_rec_is_for_blob( This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED transactions. */ - return(FALSE); + return false; } len = btr_copy_externally_stored_field_prefix( - buf, prefix_len, dict_tf_get_page_size(table->flags), + buf, prefix_len ? prefix_len : sizeof buf, + dict_tf_get_page_size(table->flags), clust_field, clust_len); if (len == 0) { @@ -134,11 +134,18 @@ row_sel_sec_rec_is_for_blob( referring to this clustered index record, because btr_free_externally_stored_field() is called after all secondary index entries of the row have been purged. */ - return(FALSE); + return false; } - len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, - prefix_len, len, (const char*) buf); + if (prefix_len) { + len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, + prefix_len, len, + reinterpret_cast + (buf)); + } else if (len >= sizeof buf) { + ut_ad("too long column" == 0); + return false; + } return(!cmp_data_data(mtype, prtype, buf, len, sec_field, sec_len)); } @@ -218,6 +225,8 @@ row_sel_sec_rec_is_for_clust_rec( ifield = dict_index_get_nth_field(sec_index, i); col = dict_field_get_col(ifield); + sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); + is_virtual = dict_col_is_virtual(col); /* For virtual column, its value will need to be @@ -250,43 +259,54 @@ row_sel_sec_rec_is_for_clust_rec( innobase_report_computed_value_failed(row); return DB_COMPUTE_VALUE_FAILED; } - clust_len = vfield->len; + len = clust_len = vfield->len; clust_field = static_cast(vfield->data); } else { clust_pos = dict_col_get_clust_pos(col, clust_index); clust_field = rec_get_nth_field( clust_rec, clust_offs, clust_pos, &clust_len); - } - - sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); - - len = clust_len; - - if (ifield->prefix_len > 0 && len != UNIV_SQL_NULL - && sec_len != UNIV_SQL_NULL && !is_virtual) { + if (clust_len == UNIV_SQL_NULL) { + if (sec_len == UNIV_SQL_NULL) { + continue; + } + return DB_SUCCESS; + } + if (sec_len == UNIV_SQL_NULL) { + return DB_SUCCESS; + } + len = clust_len; if (rec_offs_nth_extern(clust_offs, clust_pos)) { len -= BTR_EXTERN_FIELD_REF_SIZE; } - len = dtype_get_at_most_n_mbchars( - col->prtype, col->mbminlen, col->mbmaxlen, - ifield->prefix_len, len, (char*) clust_field); - - if (rec_offs_nth_extern(clust_offs, clust_pos) - && len < sec_len) { - if (!row_sel_sec_rec_is_for_blob( - col->mtype, col->prtype, - col->mbminlen, col->mbmaxlen, - clust_field, clust_len, - sec_field, sec_len, - ifield->prefix_len, - clust_index->table)) { - return DB_SUCCESS; + if (ulint prefix_len = ifield->prefix_len) { + len = dtype_get_at_most_n_mbchars( + col->prtype, col->mbminlen, + col->mbmaxlen, prefix_len, len, + reinterpret_cast( + clust_field)); + if (len < sec_len) { + goto check_for_blob; } + } else { +check_for_blob: + if (rec_offs_nth_extern(clust_offs, + clust_pos)) { + if (!row_sel_sec_rec_is_for_blob( + col->mtype, col->prtype, + col->mbminlen, + col->mbmaxlen, + clust_field, clust_len, + sec_field, sec_len, + prefix_len, + clust_index->table)) { + return DB_SUCCESS; + } - continue; + continue; + } } } From 14a18d7d7f6293ad0e106288eab4fdcb3a72ebd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 24 Apr 2021 09:37:46 +0300 Subject: [PATCH 13/14] MDEV-23026/MDEV-25474 fixup: Assertion ib_table->stat_initialized It is possible that an object that was originally created by open_purge_table() will remain cached and reused for SQL execution. Our previous fix wrongly assumed that ha_innobase::open() would always be called before SQL execution starts. Therefore, we must invoke dict_stats_init() in ha_innobase::info_low() instead of only doing it in ha_innobase::open(). Note: Concurrent execution of dict_stats_init() on the same table is possible, but it also was possible between two calls to ha_innobase::open(), with no ill effects observed. This should fix the assertion failure on stat_initialized. A possibly easy way to reproduce it would have been to run the server with innodb_force_recovery=2 (disable the purge of history), update a table so that an indexed virtual column will be affected, and finally restart the server normally (purge enabled), to observe a crash when the table is accessed from SQL. The problem was first observed and this fix verified by Elena Stepanova. Also Thirunarayanan Balathandayuthapani repeated the problem. --- storage/innobase/handler/ha_innodb.cc | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a725569fa6e..91d12f64fa5 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6261,16 +6261,6 @@ no_such_table: innobase_copy_frm_flags_from_table_share(ib_table, table->s); - const bool bk_thread = THDVAR(thd, background_thread); - /* No point to init any statistics if tablespace is still encrypted - or if table is being opened by background thread */ - if (bk_thread) { - } else if (ib_table->is_readable()) { - dict_stats_init(ib_table); - } else { - ib_table->stat_initialized = 1; - } - MONITOR_INC(MONITOR_TABLE_OPEN); bool no_tablespace = false; @@ -6516,7 +6506,7 @@ no_such_table: } } - if (!bk_thread) { + if (!THDVAR(thd, background_thread)) { info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); } @@ -14399,6 +14389,10 @@ ha_innobase::info_low( ib_table = m_prebuilt->table; DBUG_ASSERT(ib_table->get_ref_count() > 0); + if (!ib_table->is_readable()) { + ib_table->stat_initialized = true; + } + if (flag & HA_STATUS_TIME) { if (is_analyze || innobase_stats_on_metadata) { @@ -14449,6 +14443,8 @@ ha_innobase::info_low( stats.update_time = (ulong) ib_table->update_time; } + dict_stats_init(ib_table); + if (flag & HA_STATUS_VARIABLE) { ulint stat_clustered_index_size; From 2c9bf0ae8758b2c46ea5e02d1ea3d3ab5cab63b2 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 24 Apr 2021 15:50:25 -0700 Subject: [PATCH 14/14] This commit adds the same call of st_select_lex::set_unique_exclude() that complemented the fix for MDEV-24823 in 10.2. As it is the only call of this function in 10.3 the commit also has added the code of the function. --- sql/sql_delete.cc | 2 +- sql/sql_lex.cc | 21 +++++++++++++++++++++ sql/sql_lex.h | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c78e8ed510e..4d6a2c64e3e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1013,7 +1013,7 @@ int mysql_multi_delete_prepare(THD *thd) Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->select_lex.set_unique_exclude(); /* Fix tables-to-be-deleted-from list to point at opened tables */ for (target_tbl= (TABLE_LIST*) aux_tables; target_tbl; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fe4d086bd22..56046584b8b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4927,6 +4927,27 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) } +/** + Set exclude_from_table_unique_test for selects of this select and all selects + belonging to the underlying units of derived tables or views +*/ + +void st_select_lex::set_unique_exclude() +{ + exclude_from_table_unique_test= TRUE; + for (SELECT_LEX_UNIT *unit= first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (unit->derived && unit->derived->is_view_or_derived()) + { + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + sl->set_unique_exclude(); + } + } +} + + /* Return true if this select_lex has been converted into a semi-join nest within 'ancestor'. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 710c17a51d8..16768e0300f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1354,6 +1354,8 @@ public: bool save_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd); + void set_unique_exclude(); + bool is_merged_child_of(st_select_lex *ancestor); /*