From 0f56e21efa68ba3b37d1171d001c21845c3d2b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 16 Mar 2022 11:49:47 +0200 Subject: [PATCH 01/28] MDEV-28091 PERFORMANCE_SCHEMA unit tests fail due to memory misalignment Let us make the mocked-up pfs_malloc() return aligned memory, just like the actual implementation does. --- storage/perfschema/unittest/stub_pfs_global.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/storage/perfschema/unittest/stub_pfs_global.h b/storage/perfschema/unittest/stub_pfs_global.h index 8a1f9216ba2..b7adbe33504 100644 --- a/storage/perfschema/unittest/stub_pfs_global.h +++ b/storage/perfschema/unittest/stub_pfs_global.h @@ -1,4 +1,5 @@ /* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2022, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -24,6 +25,9 @@ #include #include #include +#ifdef HAVE_MEMALIGN +# include +#endif bool pfs_initialized= false; @@ -43,7 +47,17 @@ void *pfs_malloc(size_t size, myf) if (--stub_alloc_fails_after_count <= 0) return NULL; +#ifndef PFS_ALIGNEMENT void *ptr= malloc(size); +#elif defined HAVE_MEMALIGN + void *ptr= memalign(PFS_ALIGNEMENT, size); +#elif defined HAVE_ALIGNED_MALLOC + void *ptr= _aligned_malloc(size, PFS_ALIGNEMENT); +#else + void *ptr; + if (posix_memalign(&ptr, PFS_ALIGNEMENT, size)) + ptr= NULL; +#endif if (ptr != NULL) memset(ptr, 0, size); return ptr; From 75e39f3cba01f9f3a835d8e311658bcbf0453d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 17 Mar 2022 10:13:50 +0200 Subject: [PATCH 02/28] Fix gcc-12 -O2 -Wmaybe-uninitialized --- storage/innobase/fil/fil0fil.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 8075defac4c..6980078f87d 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2021, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2021, MariaDB Corporation. +Copyright (c) 2014, 2022, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3316,10 +3316,11 @@ fil_make_filepath( if (path != NULL) { memcpy(full_name, path, path_len); len = path_len; - full_name[len] = '\0'; - os_normalize_path(full_name); } + full_name[len] = '\0'; + os_normalize_path(full_name); + if (trim_name) { /* Find the offset of the last DIR separator and set it to null in order to strip off the old basename from this path. */ From 118826d1734bc4f650f9ec96b3d0d885eedba9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 17 Mar 2022 10:20:07 +0200 Subject: [PATCH 03/28] Fix gcc-12 -O2 -Warray-bounds --- sql/handler.h | 4 +-- sql/sql_table.cc | 43 +++++++++++++++++---------------- storage/innobase/fts/fts0fts.cc | 4 +-- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index 02a4a76c6c1..27836f1735f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2,7 +2,7 @@ #define HANDLER_INCLUDED /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2009, 2021, MariaDB + Copyright (c) 2009, 2022, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -4275,7 +4275,7 @@ static inline const char *ha_resolve_storage_engine_name(const handlerton *db_ty static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag) { - return db_type == NULL ? FALSE : MY_TEST(db_type->flags & flag); + return db_type && (db_type->flags & flag); } static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e1f752191ae..a2dc5c97aeb 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9191,22 +9191,24 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT; } + handlerton * const old_db_type= table->s->db_type(); + handlerton *new_db_type= create_info->db_type; + DBUG_PRINT("info", ("old type: %s new type: %s", - ha_resolve_storage_engine_name(table->s->db_type()), - ha_resolve_storage_engine_name(create_info->db_type))); - if (ha_check_storage_engine_flag(table->s->db_type(), HTON_ALTER_NOT_SUPPORTED)) + ha_resolve_storage_engine_name(old_db_type), + ha_resolve_storage_engine_name(new_db_type))); + if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED)) { DBUG_PRINT("info", ("doesn't support alter")); - my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str, + my_error(ER_ILLEGAL_HA, MYF(0), hton_name(old_db_type)->str, alter_ctx.db, alter_ctx.table_name); DBUG_RETURN(true); } - if (ha_check_storage_engine_flag(create_info->db_type, - HTON_ALTER_NOT_SUPPORTED)) + if (ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) { DBUG_PRINT("info", ("doesn't support alter")); - my_error(ER_ILLEGAL_HA, MYF(0), hton_name(create_info->db_type)->str, + my_error(ER_ILLEGAL_HA, MYF(0), hton_name(new_db_type)->str, alter_ctx.new_db, alter_ctx.new_name); DBUG_RETURN(true); } @@ -9349,6 +9351,17 @@ do_continue:; DBUG_RETURN(true); } } + /* + If the old table had partitions and we are doing ALTER TABLE ... + engine= , the new table must preserve the original + partitioning. This means that the new engine is still the + partitioning engine, not the engine specified in the parser. + This is discovered in prep_alter_part_table, which in such case + updates create_info->db_type. + It's therefore important that the assignment below is done + after prep_alter_part_table. + */ + new_db_type= create_info->db_type; #endif if (mysql_prepare_alter_table(thd, table, create_info, alter_info, @@ -9424,7 +9437,7 @@ do_continue:; Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) || is_inplace_alter_impossible(table, create_info, alter_info) || IF_PARTITIONING((partition_changed && - !(table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)), 0)) + !(old_db_type->partition_flags() & HA_USE_AUTO_PARTITION)), 0)) { if (alter_info->requested_algorithm == Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) @@ -9441,22 +9454,10 @@ do_continue:; request table rebuild. Set ALTER_RECREATE flag to force table rebuild. */ - if (create_info->db_type == table->s->db_type() && + if (new_db_type == old_db_type && create_info->used_fields & HA_CREATE_USED_ENGINE) alter_info->flags|= Alter_info::ALTER_RECREATE; - /* - If the old table had partitions and we are doing ALTER TABLE ... - engine= , the new table must preserve the original - partitioning. This means that the new engine is still the - partitioning engine, not the engine specified in the parser. - This is discovered in prep_alter_part_table, which in such case - updates create_info->db_type. - It's therefore important that the assignment below is done - after prep_alter_part_table. - */ - handlerton *new_db_type= create_info->db_type; - handlerton *old_db_type= table->s->db_type(); TABLE *new_table= NULL; ha_rows copied=0,deleted=0; diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 3cb15d64e91..6f9349a9c35 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -2313,9 +2313,7 @@ fts_trx_table_create( fts_trx_table_t* ftt; ftt = static_cast( - mem_heap_alloc(fts_trx->heap, sizeof(*ftt))); - - memset(ftt, 0x0, sizeof(*ftt)); + mem_heap_zalloc(fts_trx->heap, sizeof *ftt)); ftt->table = table; ftt->fts_trx = fts_trx; From 22fd31c5883622b5c7451cee74bc5d087d81e112 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 16 Mar 2022 14:37:55 +0400 Subject: [PATCH 04/28] MDEV-28078 Garbage on multiple equal ENUMs with tricky character sets TYPELIBs for ENUM/SET columns could erroneously undergo redundant hex-unescaping at the table open time. Fix: - Prevent multiple unescaping of the same TYPELIB - Prevent sharing TYPELIBs between columns with different mbminlen --- mysql-test/r/ctype_utf32.result | 25 +++++++++++++++++++++++++ mysql-test/t/ctype_utf32.test | 19 +++++++++++++++++++ sql/table.cc | 20 ++++++++++++++++++-- sql/unireg.cc | 11 ++++++++++- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/ctype_utf32.result b/mysql-test/r/ctype_utf32.result index 143fff9e419..22cea274182 100644 --- a/mysql-test/r/ctype_utf32.result +++ b/mysql-test/r/ctype_utf32.result @@ -2913,5 +2913,30 @@ t1 CREATE TABLE `t1` ( DROP TABLE t1; SET NAMES utf8; # +# MDEV-28078 Garbage on multiple equal ENUMs with tricky character sets +# +CREATE TABLE t1 ( +c1 ENUM ('a','b') CHARACTER SET utf32 DEFAULT 'a', +c2 ENUM ('a','b') CHARACTER SET utf32 DEFAULT 'a' +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` enum('a','b') CHARACTER SET utf32 DEFAULT 'a', + `c2` enum('a','b') CHARACTER SET utf32 DEFAULT 'a' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 ( +c1 ENUM ('00000061','00000062') DEFAULT '00000061' COLLATE latin1_bin, +c2 ENUM ('a','b') DEFAULT 'a' COLLATE utf32_general_ci +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` enum('00000061','00000062') CHARACTER SET latin1 COLLATE latin1_bin DEFAULT '00000061', + `c2` enum('a','b') CHARACTER SET utf32 DEFAULT 'a' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# # End of 10.2 tests # diff --git a/mysql-test/t/ctype_utf32.test b/mysql-test/t/ctype_utf32.test index 46ff333b5f7..739096ae9cb 100644 --- a/mysql-test/t/ctype_utf32.test +++ b/mysql-test/t/ctype_utf32.test @@ -1067,6 +1067,25 @@ DROP TABLE t1; SET NAMES utf8; +--echo # +--echo # MDEV-28078 Garbage on multiple equal ENUMs with tricky character sets +--echo # + +CREATE TABLE t1 ( + c1 ENUM ('a','b') CHARACTER SET utf32 DEFAULT 'a', + c2 ENUM ('a','b') CHARACTER SET utf32 DEFAULT 'a' +); +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 ( + c1 ENUM ('00000061','00000062') DEFAULT '00000061' COLLATE latin1_bin, + c2 ENUM ('a','b') DEFAULT 'a' COLLATE utf32_general_ci +); +SHOW CREATE TABLE t1; +DROP TABLE t1; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/table.cc b/sql/table.cc index ca6ce02e4f2..1f7b6452303 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1229,6 +1229,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, plugin_ref se_plugin= 0; MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; + bool *interval_unescaped= NULL; DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); keyinfo= &first_keyinfo; @@ -1686,6 +1687,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, goto err; + if (interval_count) + { + if (!(interval_unescaped= (bool*) my_alloca(interval_count * sizeof(bool)))) + goto err; + bzero(interval_unescaped, interval_count * sizeof(bool)); + } + field_ptr= share->field; table_check_constraints= share->check_constraints; read_length=(uint) (share->fields * field_pack_length + @@ -1956,11 +1964,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (share->mysql_version < 100200) pack_flag&= ~FIELDFLAG_LONG_DECIMAL; - if (interval_nr && charset->mbminlen > 1) + if (interval_nr && charset->mbminlen > 1 && + !interval_unescaped[interval_nr - 1]) { - /* Unescape UCS2 intervals from HEX notation */ + /* + Unescape UCS2/UTF16/UTF32 intervals from HEX notation. + Note, ENUM/SET columns with equal value list share a single + copy of TYPELIB. Unescape every TYPELIB only once. + */ TYPELIB *interval= share->intervals + interval_nr - 1; unhex_type2(interval); + interval_unescaped[interval_nr - 1]= true; } #ifndef TO_BE_DELETED_ON_PRODUCTION @@ -2610,6 +2624,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->error= OPEN_FRM_OK; thd->status_var.opened_shares++; thd->mem_root= old_root; + my_afree(interval_unescaped); DBUG_RETURN(0); err: @@ -2623,6 +2638,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, open_table_error(share, OPEN_FRM_CORRUPTED, share->open_errno); thd->mem_root= old_root; + my_afree(interval_unescaped); DBUG_RETURN(HA_ERR_NOT_A_TABLE); } diff --git a/sql/unireg.cc b/sql/unireg.cc index 7974255af35..5471290651b 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -757,7 +757,16 @@ static uint get_interval_id(uint *int_count,List &create_fields, while ((field=it++) != last_field) { - if (field->interval_id && field->interval->count == interval->count) + /* + ENUM/SET columns with equal value lists share a single + copy of the underlying TYPELIB. + Fields with different mbminlen can't reuse TYPELIBs, because: + - mbminlen==1 are written to FRM as is + - mbminlen>1 are written to FRM in hex-encoded format + */ + if (field->interval_id && + field->interval->count == interval->count && + field->charset->mbminlen == last_field->charset->mbminlen) { const char **a,**b; for (a=field->interval->type_names, b=interval->type_names ; From ecb6f9c894d3ebafeff1c6eb3b65cd248062296f Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 17 Mar 2022 16:57:56 +0100 Subject: [PATCH 05/28] MDEV-28095 crash in multi-update and implicit grouping disallow implicit grouping in multi-update. explicit GROUP BY is not allowed by the grammar. --- mysql-test/main/multi_update_innodb.result | 15 +++++++++++++++ mysql-test/main/multi_update_innodb.test | 19 +++++++++++++++++++ sql/sql_update.cc | 5 +++++ 3 files changed, 39 insertions(+) diff --git a/mysql-test/main/multi_update_innodb.result b/mysql-test/main/multi_update_innodb.result index 2ec7eb3065e..52bbece4fa0 100644 --- a/mysql-test/main/multi_update_innodb.result +++ b/mysql-test/main/multi_update_innodb.result @@ -207,4 +207,19 @@ ERROR 23000: Duplicate entry '0000-00-00 00:00:00' for key 'f2k' DROP VIEW v1; DROP TABLE t3,t4; SET @@sql_mode=@save_sql_mode; +# # End of 10.2 tests +# +# +# MDEV-28095 crash in multi-update and implicit grouping +# +CREATE TABLE t1 (a int) engine=innodb; +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (b int); +INSERT INTO t2 VALUES (1),(2); +UPDATE t1 NATURAL JOIN t2 SET a = 1 ORDER BY AVG (a) ; +ERROR HY000: Invalid use of group function +DROP TABLE t1, t2; +# +# End of 10.3 tests +# diff --git a/mysql-test/main/multi_update_innodb.test b/mysql-test/main/multi_update_innodb.test index 04736482011..02f6a7a3316 100644 --- a/mysql-test/main/multi_update_innodb.test +++ b/mysql-test/main/multi_update_innodb.test @@ -243,4 +243,23 @@ DROP VIEW v1; DROP TABLE t3,t4; SET @@sql_mode=@save_sql_mode; +--echo # --echo # End of 10.2 tests +--echo # + +--echo # +--echo # MDEV-28095 crash in multi-update and implicit grouping +--echo # +CREATE TABLE t1 (a int) engine=innodb; +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (b int); +INSERT INTO t2 VALUES (1),(2); +--error ER_INVALID_GROUP_FUNC_USE +UPDATE t1 NATURAL JOIN t2 SET a = 1 ORDER BY AVG (a) ; +DROP TABLE t1, t2; + + +--echo # +--echo # End of 10.3 tests +--echo # + diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1e997b75c7d..a6a0b78259d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -2150,6 +2150,11 @@ multi_update::initialize_tables(JOIN *join) if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))) DBUG_RETURN(1); + if (join->implicit_grouping) + { + my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); + DBUG_RETURN(1); + } main_table=join->join_tab->table; table_to_update= 0; From 74e668eaeb4271845fd69d2945085383c431e333 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 17 Mar 2022 16:58:43 +0200 Subject: [PATCH 06/28] Fixed warning for maria.maria-recovery2 about crashed table The bug was a missing va_start in eprint() which caused a wrong table name to be printed. Patch backported from 10.3. --- storage/maria/ma_recovery_util.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/storage/maria/ma_recovery_util.c b/storage/maria/ma_recovery_util.c index 9443ba90f6c..cb4203392dd 100644 --- a/storage/maria/ma_recovery_util.c +++ b/storage/maria/ma_recovery_util.c @@ -59,11 +59,12 @@ void tprint(FILE *trace_file __attribute__ ((unused)), va_list args; #ifndef DBUG_OFF { - char buff[1024], *end; + char buff[1024]; + size_t length; va_start(args, format); - vsnprintf(buff, sizeof(buff)-1, format, args); - if (*(end= strend(buff)) == '\n') - *end= 0; /* Don't print end \n */ + length= my_vsnprintf(buff, sizeof(buff)-1, format, args); + if (length && buff[length-1] == '\n') + buff[length-1]= 0; /* Don't print end \n */ DBUG_PRINT("info", ("%s", buff)); va_end(args); } @@ -95,6 +96,7 @@ void eprint(FILE *trace_file __attribute__ ((unused)), fputc('\n', trace_file); if (trace_file != stderr) { + va_start(args, format); my_printv_error(HA_ERR_INITIALIZATION, format, MYF(0), args); } va_end(args); From cf86580f2b4ca2b7bd337d27af22b46c82bb25df Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Thu, 10 Mar 2022 12:07:38 +0900 Subject: [PATCH 07/28] MDEV-28032 "git submodule update --depth 1" may fail with old Git submodules.cmake: don't use "--depth 1" with old Git Old Git may not work with "--depth 1" when the referenced commit hash is far from HEAD. Newer Git improves the situation. For example: https://github.com/git/git/commit/fb43e31f2b43076e7a30c9cd00d0241cb8cf97eb It's safe to not use "--depth 1" with old Git. Closes #2049 --- cmake/submodules.cmake | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/cmake/submodules.cmake b/cmake/submodules.cmake index b6c64b32f3d..fca67a0508b 100644 --- a/cmake/submodules.cmake +++ b/cmake/submodules.cmake @@ -17,20 +17,29 @@ IF(GIT_EXECUTABLE AND EXISTS "${CMAKE_SOURCE_DIR}/.git") ${GIT_EXECUTABLE} config cmake.update-submodules yes") ELSEIF(git_config_get_result EQUAL 128) SET(update_result 0) - ELSEIF (cmake_update_submodules MATCHES force) - MESSAGE(STATUS "Updating submodules (forced)") - EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --force --depth=1 - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE update_result) - ELSEIF (cmake_update_submodules MATCHES yes) - EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --depth=1 - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE update_result) ELSE() - MESSAGE(STATUS "Updating submodules") - EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --depth=1 - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE update_result) + SET(UPDATE_SUBMODULES_COMMAND + "${GIT_EXECUTABLE}" submodule update --init --recursive) + # Old Git may not work with "--depth 1". + # See also: https://github.com/git/git/commit/fb43e31f2b43076e7a30c9cd00d0241cb8cf97eb + IF(NOT GIT_VERSION_STRING VERSION_LESS "2.8.0") + SET(UPDATE_SUBMODULES_COMMAND ${UPDATE_SUBMODULES_COMMAND} --depth 1) + ENDIF() + IF(cmake_update_submodules MATCHES force) + MESSAGE(STATUS "Updating submodules (forced)") + EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} --force + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE update_result) + ELSEIF(cmake_update_submodules MATCHES yes) + EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE update_result) + ELSE() + MESSAGE(STATUS "Updating submodules") + EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE update_result) + ENDIF() ENDIF() ENDIF() From 421153848f0002c9e9b5e204d4c6e85dfcfb5600 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 20 Mar 2022 21:36:41 +0100 Subject: [PATCH 08/28] MDEV-27980 file-key-management plugin disabled in mysql_install_db breaks automated deployments (and container initialization) Revert "Silence the file-key-management plugin during mysql_install_db" This reverts commit e99d3da6381023395c86f679bb76b00b4385dc2d. --- scripts/mysql_install_db.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh index 3df48f0eb95..bf2e2617b9b 100644 --- a/scripts/mysql_install_db.sh +++ b/scripts/mysql_install_db.sh @@ -506,7 +506,7 @@ mysqld_install_cmd_line() { "$mysqld_bootstrap" $defaults $defaults_group_suffix "$mysqld_opt" --bootstrap $silent_startup\ "--basedir=$basedir" "--datadir=$ldata" --log-warnings=0 --enforce-storage-engine="" \ - "--plugin-dir=${plugindir}" --loose-disable-plugin-file-key-management \ + "--plugin-dir=${plugindir}" \ $args --max_allowed_packet=8M \ --net_buffer_length=16K } From fbc1cc974e433ad4ee77ef19a2ea199537686a98 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 18 Mar 2022 11:13:09 +0100 Subject: [PATCH 09/28] MDEV-26009 Server crash when calling twice procedure using FOR-loop The problem was that instructions sp_instr_cursor_copy_struct and sp_instr_copen uses the same lex, adding and removing "tail" of prelocked tables and forgetting that tail of all tables is kept in LEX::query_tables_last. If the LEX used only by one instruction or the query do not have prelocked tables it is not important. But to work correctly in all cases LEX::query_tables_last should be reset to make new tables added in the correct list (after last table in the LEX instead after last table of the prelocking "tail" which was cut). --- mysql-test/main/sp-cursor.result | 63 ++++++++++++++++++++++++++++++++ mysql-test/main/sp-cursor.test | 56 ++++++++++++++++++++++++++++ sql/sp_head.cc | 1 + 3 files changed, 120 insertions(+) diff --git a/mysql-test/main/sp-cursor.result b/mysql-test/main/sp-cursor.result index 2656ef8821d..b1c2b335ea4 100644 --- a/mysql-test/main/sp-cursor.result +++ b/mysql-test/main/sp-cursor.result @@ -737,3 +737,66 @@ rec.en1 c DROP PROCEDURE p1; DROP TABLE t1; +# +# MDEV-26009: Server crash when calling twice procedure using FOR-loop +# +CREATE TABLE t1 ( id int, name varchar(24)); +INSERT INTO t1 values (1, 'x'), (2, 'y'), (3, 'z'); +create function get_name(_id int) returns varchar(24) +return (select name from t1 where id = _id); +select get_name(id) from t1; +get_name(id) +x +y +z +create procedure test_proc() +begin +declare _cur cursor for select get_name(id) from t1; +for row in _cur do select 1; end for; +end; +^^ +call test_proc(); +1 +1 +1 +1 +1 +1 +call test_proc(); +1 +1 +1 +1 +1 +1 +drop procedure test_proc; +drop function get_name; +drop table t1; +CREATE TABLE t1 (id int, name varchar(24)); +INSERT INTO t1 (id, name) VALUES (1, 'x'),(2, 'y'),(3, 'z'); +create function get_name(_id int) returns varchar(24) +return (select name from t1 where id = _id); +create view v1 as select get_name(id) from t1; +create procedure test_proc() +begin +declare _cur cursor for select 1 from v1; +for row in _cur do select 1; end for; +end$$ +call test_proc(); +1 +1 +1 +1 +1 +1 +call test_proc(); +1 +1 +1 +1 +1 +1 +drop procedure test_proc; +drop view v1; +drop function get_name; +drop table t1; diff --git a/mysql-test/main/sp-cursor.test b/mysql-test/main/sp-cursor.test index 97483ef9caf..9794815c784 100644 --- a/mysql-test/main/sp-cursor.test +++ b/mysql-test/main/sp-cursor.test @@ -744,3 +744,59 @@ DELIMITER ;$$ CALL p1(); DROP PROCEDURE p1; DROP TABLE t1; + + +--echo # +--echo # MDEV-26009: Server crash when calling twice procedure using FOR-loop +--echo # + + +CREATE TABLE t1 ( id int, name varchar(24)); +INSERT INTO t1 values (1, 'x'), (2, 'y'), (3, 'z'); + +create function get_name(_id int) returns varchar(24) + return (select name from t1 where id = _id); + +select get_name(id) from t1; + +delimiter ^^; + +create procedure test_proc() +begin + declare _cur cursor for select get_name(id) from t1; + for row in _cur do select 1; end for; +end; +^^ +delimiter ;^^ + +call test_proc(); +call test_proc(); + +drop procedure test_proc; +drop function get_name; +drop table t1; + + +CREATE TABLE t1 (id int, name varchar(24)); +INSERT INTO t1 (id, name) VALUES (1, 'x'),(2, 'y'),(3, 'z'); + +create function get_name(_id int) returns varchar(24) + return (select name from t1 where id = _id); + +create view v1 as select get_name(id) from t1; + +delimiter $$; +create procedure test_proc() +begin + declare _cur cursor for select 1 from v1; + for row in _cur do select 1; end for; +end$$ +delimiter ;$$ + +call test_proc(); +call test_proc(); + +drop procedure test_proc; +drop view v1; +drop function get_name; +drop table t1; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 57ab31d9edf..1d1199aaa62 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3486,6 +3486,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, lex_query_tables_own_last= m_lex->query_tables_own_last; prelocking_tables= *lex_query_tables_own_last; *lex_query_tables_own_last= NULL; + m_lex->query_tables_last= m_lex->query_tables_own_last; m_lex->mark_as_requiring_prelocking(NULL); } thd->rollback_item_tree_changes(); From f54d6380d2662d7bc7f173bf96f5dc3d7cf3aec1 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 21 Mar 2022 11:01:40 +0100 Subject: [PATCH 10/28] MDEV-27980 file-key-management plugin disabled in mysql_install_db breaks automated deployments (and container initialization) fix a 2015 typo in build scripts. --without-plugin=plugin_file_key_management translates to -DPLUGIN_PLUGIN_FILE_KEY_MANAGEMENT=NO replace it with a line from 10.4 that builds the plugin dynamically. --- BUILD/SETUP.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 8a7238c359f..77504522f11 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -211,7 +211,7 @@ fi max_no_embedded_configs="$SSL_LIBRARY --with-plugins=max" max_no_qc_configs="$SSL_LIBRARY --with-plugins=max --without-query-cache" -max_configs="$SSL_LIBRARY --with-plugins=max --with-embedded-server --with-libevent --without-plugin=plugin_file_key_management --with-plugin-rocksdb=dynamic --with-plugin-test_sql_discovery=DYNAMIC" +max_configs="$SSL_LIBRARY --with-plugins=max --with-embedded-server --with-libevent --with-plugin-rocksdb=dynamic --with-plugin-test_sql_discovery=DYNAMIC --with-plugin-file_key_management=DYNAMIC" all_configs="$SSL_LIBRARY --with-plugins=max --with-embedded-server --with-innodb_plugin --with-libevent" # From 6277e7df6b84f6d5931dab66d3edf8859d5b16d3 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 22 Nov 2021 09:58:46 +0400 Subject: [PATCH 11/28] MDEV-22742 UBSAN: Many overflow issues in strings/decimal.c - runtime error: signed integer overflow: x * y cannot be represented in type 'long long int' (on optimized builds). Avoid integer overflow, do the check before the calculation. --- strings/decimal.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/strings/decimal.c b/strings/decimal.c index 9d18a9ce52a..78b4c12b0ef 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -1128,13 +1128,21 @@ int decimal2ulonglong(const decimal_t *from, ulonglong *to) for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1) { - ulonglong y=x; - x=x*DIG_BASE + *buf++; - if (unlikely(y > ((ulonglong) ULONGLONG_MAX/DIG_BASE) || x < y)) + /* + Check that the decimal is bigger than any possible integer. + Do it before we do the x*=DIB_BASE to avoid integer + overflow. + */ + if (unlikely ( + x >= ULONGLONG_MAX/DIG_BASE && + (x > ULONGLONG_MAX/DIG_BASE || + *buf > (dec1) (ULONGLONG_MAX%DIG_BASE)))) { *to=ULONGLONG_MAX; return E_DEC_OVERFLOW; } + + x=x*DIG_BASE + *buf++; } *to=x; for (frac=from->frac; unlikely(frac > 0); frac-=DIG_PER_DEC1) @@ -1151,23 +1159,29 @@ int decimal2longlong(const decimal_t *from, longlong *to) for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1) { - longlong y=x; /* + Check that the decimal is less than any possible integer. + Do it before we do the x*=DIB_BASE to avoid integer + overflow. Attention: trick! we're calculating -|from| instead of |from| here because |LONGLONG_MIN| > LONGLONG_MAX - so we can convert -9223372036854775808 correctly + so we can convert -9223372036854775808 correctly. */ - x=x*DIG_BASE - *buf++; - if (unlikely(y < (LONGLONG_MIN/DIG_BASE) || x > y)) + if (unlikely ( + x <= LONGLONG_MIN/DIG_BASE && + (x < LONGLONG_MIN/DIG_BASE || + *buf > (dec1) (-(LONGLONG_MIN%DIG_BASE))))) { /* - the decimal is bigger than any possible integer - return border integer depending on the sign + the decimal is bigger than any possible integer + return border integer depending on the sign */ *to= from->sign ? LONGLONG_MIN : LONGLONG_MAX; return E_DEC_OVERFLOW; } + + x=x*DIG_BASE - *buf++; } /* boundary case: 9223372036854775808 */ if (unlikely(from->sign==0 && x == LONGLONG_MIN)) From 0812d0de8dcb1f76d4a03cea3f20bfa30345b83b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 21 Mar 2022 16:42:58 +0400 Subject: [PATCH 12/28] MDEV-28131 Unexpected warning while selecting from information_schema.processlist Problem: DECIMAL columns in I_S must be explicitly set of some value. I_S columns do not have `DEFAULT 0` (after MDEV-18918), so during restore_record() their record fragments pointed by Field::ptr are initialized to zero bytes 0x00. But an array of 0x00's is not a valid binary DECIMAL value. So val_decimal() called for such Field_new_decimal generated a warning when seeing a wrong binary encoded DECIMAL value in the record. Fix: Explicitly setting INFORMATION_SCHEMA.PROCESSLIST.PROGRESS to the decimal value of 0 if no progress information is available. --- mysql-test/main/processlist.result | 20 +++++++++++++++++ mysql-test/main/processlist.test | 35 ++++++++++++++++++++++++++++++ sql/sql_show.cc | 10 +++++++++ 3 files changed, 65 insertions(+) diff --git a/mysql-test/main/processlist.result b/mysql-test/main/processlist.result index 2d3228a6d91..d99160f5c74 100644 --- a/mysql-test/main/processlist.result +++ b/mysql-test/main/processlist.result @@ -40,3 +40,23 @@ utf8mb4_string xxx😎yyy # # End of 10.1 tests # +# +# Start of 10.3 tests +# +# +# MDEV-28131 Unexpected warning while selecting from information_schema.processlist +# +connect conn1, localhost, root,,; +connection conn1; +SELECT SLEEP(1000); +connection default; +SELECT progress FROM information_schema.processlist WHERE info='SELECT SLEEP(1000)'; +progress +0.000 +connection conn1; +Got one of the listed errors +connection default; +disconnect conn1; +# +# End of 10.3 tests +# diff --git a/mysql-test/main/processlist.test b/mysql-test/main/processlist.test index 8e98701459a..f419f57ea2f 100644 --- a/mysql-test/main/processlist.test +++ b/mysql-test/main/processlist.test @@ -70,3 +70,38 @@ SELECT INFO, INFO_BINARY, 'xxx😎yyy' AS utf8mb4_string FROM INFORMATION_SCHEMA --echo # --echo # End of 10.1 tests --echo # + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-28131 Unexpected warning while selecting from information_schema.processlist +--echo # + +connect (conn1, localhost, root,,); +connection conn1; +let $ID= `select connection_id()`; +send SELECT SLEEP(1000); +connection default; +let $wait_timeout= 10; +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='User sleep' and info='SELECT SLEEP(1000)'; +--source include/wait_condition.inc +SELECT progress FROM information_schema.processlist WHERE info='SELECT SLEEP(1000)'; +disable_query_log; +eval kill $ID; +enable_query_log; +let $wait_timeout= 10; +let $wait_condition=select count(*)=0 from information_schema.processlist +where state='User sleep' and info='SELECT SLEEP(1000)'; +--source include/wait_condition.inc +connection conn1; +--error 2013,ER_CONNECTION_KILLED +reap; +connection default; +disconnect conn1; + +--echo # +--echo # End of 10.3 tests +--echo # diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e1090d450e8..439cad1c858 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3351,6 +3351,16 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[11]->store((double) tmp->progress.counter / (double) max_counter*100.0); } + else + { + /* + This is a DECIMAL column without DEFAULT. + restore_record() fills its Field::ptr to zero bytes, + according to pack_length(). But an array of zero bytes + is not a valid decimal. Set it explicitly to 0. + */ + table->field[11]->store((longlong) 0, true); + } mysql_mutex_unlock(&tmp->LOCK_thd_data); } From 8f4d7e365e41962370f63294e9ebd8152c3c9d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 10 Feb 2022 07:33:02 +0200 Subject: [PATCH 13/28] MDEV-27775 : Some Galera tests fail on FreeBSD due to "unknown signal 9" Replace 9 with KILL --- mysql-test/suite/galera/include/kill_galera.inc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/galera/include/kill_galera.inc b/mysql-test/suite/galera/include/kill_galera.inc index d7f665df6c7..c8b88496e52 100644 --- a/mysql-test/suite/galera/include/kill_galera.inc +++ b/mysql-test/suite/galera/include/kill_galera.inc @@ -1,5 +1,10 @@ --echo Killing server ... +if (!$kill_signal) +{ +--let $kill_signal = KILL +} + # Write file to make mysql-test-run.pl expect the crash, but don't start it --let $_server_id= `SELECT @@server_id` --let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect @@ -7,13 +12,15 @@ # Kill the connected server --disable_reconnect +--let KILL_SIGNAL_VALUE = $kill_signal --let KILL_NODE_PIDFILE = `SELECT @@pid_file` --perl + my $kill_sig = $ENV{'KILL_SIGNAL_VALUE'} my $pid_filename = $ENV{'KILL_NODE_PIDFILE'}; my $mysqld_pid = `cat $pid_filename`; chomp($mysqld_pid); - system("kill -9 $mysqld_pid"); + system("kill -s $kill_sig $mysqld_pid"); exit(0); EOF From cade21b4e054486d18db9212e823ea8ce12ea46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Tue, 22 Mar 2022 09:27:43 +0200 Subject: [PATCH 14/28] MDEV-27775 : Some Galera tests fail on FreeBSD due to "unknown signal 9" Replace 9 with KILL --- mysql-test/suite/galera/include/kill_galera.inc | 2 +- .../galera/r/galera_ist_restart_joiner.result | 1 + .../suite/galera/t/galera_ist_restart_joiner.test | 14 +------------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/mysql-test/suite/galera/include/kill_galera.inc b/mysql-test/suite/galera/include/kill_galera.inc index c8b88496e52..56118df84f9 100644 --- a/mysql-test/suite/galera/include/kill_galera.inc +++ b/mysql-test/suite/galera/include/kill_galera.inc @@ -16,7 +16,7 @@ if (!$kill_signal) --let KILL_NODE_PIDFILE = `SELECT @@pid_file` --perl - my $kill_sig = $ENV{'KILL_SIGNAL_VALUE'} + my $kill_sig = $ENV{'KILL_SIGNAL_VALUE'}; my $pid_filename = $ENV{'KILL_NODE_PIDFILE'}; my $mysqld_pid = `cat $pid_filename`; chomp($mysqld_pid); diff --git a/mysql-test/suite/galera/r/galera_ist_restart_joiner.result b/mysql-test/suite/galera/r/galera_ist_restart_joiner.result index e58d04b30b3..d739558dfd5 100644 --- a/mysql-test/suite/galera/r/galera_ist_restart_joiner.result +++ b/mysql-test/suite/galera/r/galera_ist_restart_joiner.result @@ -16,6 +16,7 @@ SET SESSION wsrep_on=ON; connection node_1; UPDATE t1 SET f2 = 'd' WHERE f1 > 3; connection node_2; +Killing server ... connection node_1; UPDATE t1 SET f2 = 'e' WHERE f1 > 4; connection node_2; diff --git a/mysql-test/suite/galera/t/galera_ist_restart_joiner.test b/mysql-test/suite/galera/t/galera_ist_restart_joiner.test index 15b47a328fc..686fb0b3f76 100644 --- a/mysql-test/suite/galera/t/galera_ist_restart_joiner.test +++ b/mysql-test/suite/galera/t/galera_ist_restart_joiner.test @@ -63,19 +63,7 @@ UPDATE t1 SET f2 = 'd' WHERE f1 > 3; # Kill node #2 while IST is in progress --connection node_2 - -# Kill the connected server ---disable_reconnect - ---perl - my $pid_filename = $ENV{'KILL_NODE_PIDFILE'}; - my $mysqld_pid = `cat $pid_filename`; - chomp($mysqld_pid); - system("kill -9 $mysqld_pid"); - exit(0); -EOF - ---source include/wait_until_disconnected.inc +--source include/kill_galera.inc --connection node_1 --source include/wait_until_connected_again.inc From 8153c974e60901f5f029b925de75afa3a68f3066 Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Tue, 22 Mar 2022 14:45:55 +0200 Subject: [PATCH 15/28] Update contributors --- CREDITS | 2 ++ mysql-test/r/contributors.result | 2 ++ sql/contributors.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/CREDITS b/CREDITS index f5e87e18752..35092602ccf 100644 --- a/CREDITS +++ b/CREDITS @@ -4,9 +4,11 @@ organization registered in the USA. The current main sponsors of the MariaDB Foundation are: Alibaba Cloud https://www.alibabacloud.com/ (2017) +Intel https://www.intel.com (2022) MariaDB Corporation https://www.mariadb.com (2013) Microsoft https://microsoft.com/ (2017) ServiceNow https://servicenow.com (2019) +SIT https://sit.org (2022) Tencent Cloud https://cloud.tencent.com (2017) Development Bank of Singapore https://dbs.com (2016) IBM https://www.ibm.com (2017) diff --git a/mysql-test/r/contributors.result b/mysql-test/r/contributors.result index 0c7ca03a2c5..8d72373696c 100644 --- a/mysql-test/r/contributors.result +++ b/mysql-test/r/contributors.result @@ -5,6 +5,8 @@ Tencent Cloud https://cloud.tencent.com Platinum Sponsor of the MariaDB Foundati Microsoft https://microsoft.com/ Platinum Sponsor of the MariaDB Foundation MariaDB Corporation https://mariadb.com Founding member, Platinum Sponsor of the MariaDB Foundation ServiceNow https://servicenow.com Platinum Sponsor of the MariaDB Foundation +Intel https://www.intel.com Platinum Sponsor of the MariaDB Foundation +SIT https://sit.org Platinum Sponsor of the MariaDB Foundation Visma https://visma.com Gold Sponsor of the MariaDB Foundation DBS https://dbs.com Gold Sponsor of the MariaDB Foundation IBM https://www.ibm.com Gold Sponsor of the MariaDB Foundation diff --git a/sql/contributors.h b/sql/contributors.h index e16448ee985..bc8ba4eabbb 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -42,6 +42,8 @@ struct show_table_contributors_st show_table_contributors[]= { {"Microsoft", "https://microsoft.com/", "Platinum Sponsor of the MariaDB Foundation"}, {"MariaDB Corporation", "https://mariadb.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, {"ServiceNow", "https://servicenow.com", "Platinum Sponsor of the MariaDB Foundation"}, + {"Intel", "https://www.intel.com", "Platinum Sponsor of the MariaDB Foundation"}, + {"SIT", "https://sit.org", "Platinum Sponsor of the MariaDB Foundation"}, {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"IBM", "https://www.ibm.com", "Gold Sponsor of the MariaDB Foundation"}, From bbf02c85ba2e850da546199421cb75c224747475 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 23 Mar 2022 12:45:56 -0700 Subject: [PATCH 16/28] MDEV-24281 Reading from freed memory when running main.view with --ps-protocol This bug could affect prepared statements for the command CREATE VIEW with specification that contained unnamed basic constant in select list. If generation of a valid name for the corresponding view column required resolution of conflicts with names of other columns that were explicitly defined then execution of such prepared statement and following deallocation of this statement led to reading from freed memory. Approved by Oleksandr Byelkin --- mysql-test/main/view.result | 28 ++++++++++++++++++++++++++++ mysql-test/main/view.test | 26 ++++++++++++++++++++++++++ sql/sql_view.cc | 3 ++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/view.result b/mysql-test/main/view.result index a410ab741f5..6483d76bba1 100644 --- a/mysql-test/main/view.result +++ b/mysql-test/main/view.result @@ -6839,5 +6839,33 @@ id bar Drop View v1; Drop table t1; # +# MDEV-24281: Execution of PREPARE from CREATE VIEW statement +# +create table t1 (s1 int); +insert into t1 values (3), (7), (1); +prepare stmt from " +create view v1 as select 's1', s1, 1 as My_exp_s1 from t1; +"; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 's1' AS `My_exp_1_s1`,`t1`.`s1` AS `s1`,1 AS `My_exp_s1` from `t1` latin1 latin1_swedish_ci +select * from v1; +My_exp_1_s1 s1 My_exp_s1 +s1 3 1 +s1 7 1 +s1 1 1 +drop view v1; +prepare stmt from " +create view v1 as select 's1', s1, 1 as My_exp_s1 from t1; +"; +execute stmt; +execute stmt; +ERROR 42S01: Table 'v1' already exists +deallocate prepare stmt; +drop view v1; +drop table t1; +# # End of 10.3 tests # diff --git a/mysql-test/main/view.test b/mysql-test/main/view.test index 431dfdb86f6..46232b1bcdc 100644 --- a/mysql-test/main/view.test +++ b/mysql-test/main/view.test @@ -6576,6 +6576,32 @@ SELECT v.id, v.foo AS bar FROM v1 v Drop View v1; Drop table t1; +--echo # +--echo # MDEV-24281: Execution of PREPARE from CREATE VIEW statement +--echo # + +create table t1 (s1 int); +insert into t1 values (3), (7), (1); + +prepare stmt from " +create view v1 as select 's1', s1, 1 as My_exp_s1 from t1; +"; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from " +create view v1 as select 's1', s1, 1 as My_exp_s1 from t1; +"; +execute stmt; +--error ER_TABLE_EXISTS_ERROR +execute stmt; +deallocate prepare stmt; +drop view v1; +drop table t1; + --echo # --echo # End of 10.3 tests --echo # diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 024bd36f483..b6787a1cb8b 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -96,7 +96,8 @@ static void make_unique_view_field_name(THD *thd, Item *target, itc.rewind(); } - target->orig_name= target->name.str; + if (!target->orig_name) + target->orig_name= target->name.str; target->set_name(thd, buff, name_len, system_charset_info); } From 32ab6219bed1ca785914af5b9e4e6553e3b7a94f Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Thu, 5 Aug 2021 12:59:37 -0600 Subject: [PATCH 17/28] MDEV-25580: rpl.rpl_semi_sync_slave_compressed_protocol crashes because of wrong packet Problem: ======== When both semi-sync and slave compression are enabled, the numbering on packet headers can become out of sync between the primary and replica servers. More specifically, after the master flushes its write, it should increment the counters that track packets. The bug is such that the master only updates the normal packet counter and leaves the compressed packet counter alone. Solution: ======== After the master flushes, additionally increment the compressed packet counter. Reviewed By: ============ Andrei Elkin: --- sql/semisync_master.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc index b239a9776a7..0f868d4fc7a 100644 --- a/sql/semisync_master.cc +++ b/sql/semisync_master.cc @@ -1229,6 +1229,7 @@ int Repl_semi_sync_master::flush_net(THD *thd, net_clear(net, 0); net->pkt_nr++; + net->compress_pkt_nr++; result = 0; rpl_semi_sync_master_net_wait_num++; From 174f1734a9672c13928dfd03d49d25c4325f0ce3 Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Wed, 22 Sep 2021 11:25:52 -0600 Subject: [PATCH 18/28] MDEV-14608: mysqlbinlog lastest backupfile size is 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: ======== When using mariadb-binlog with --raw and --stop-never, events from the master's currently active log file should be written to their respective log file specified by --result-file, and shown on-disk. There is a bug where mariadb-binlog does not flush the result file to disk when new events are received Solution: ======== Add a function call to flush mariadb-binlog’s result file after receiving an event in --raw mode. Reviewed By: ============ Andrei Elkin --- client/mysqlbinlog.cc | 1 + .../r/binlog_mysqlbinlog_raw_flush.result | 7 +++ .../t/binlog_mysqlbinlog_raw_flush.test | 45 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 mysql-test/suite/binlog/r/binlog_mysqlbinlog_raw_flush.result create mode 100644 mysql-test/suite/binlog/t/binlog_mysqlbinlog_raw_flush.test diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4e28876cdf6..c7c71f6b00d 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2510,6 +2510,7 @@ static Exit_status handle_event_raw_mode(PRINT_EVENT_INFO *print_event_info, error("Could not write into log file '%s'", out_file_name); DBUG_RETURN(ERROR_STOP); } + fflush(result_file); DBUG_RETURN(OK_CONTINUE); } diff --git a/mysql-test/suite/binlog/r/binlog_mysqlbinlog_raw_flush.result b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_raw_flush.result new file mode 100644 index 00000000000..9148f0e8c2b --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_raw_flush.result @@ -0,0 +1,7 @@ +CREATE TABLE t1 (a int); +FLUSH LOGS; +INSERT INTO t1 VALUES (1); +# timeout TIMEOUT MYSQL_BINLOG --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=MASTER_MYPORT --stop-never --result-file=MYSQLTEST_VARDIR/tmp/ master-bin.000001 +# MYSQL_BINLOG MYSQLTEST_VARDIR/tmp/master-bin.000002 > MYSQLTEST_VARDIR/tmp/local-bin.000002.out +FOUND 1 /GTID 0-1-2/ in local-bin.000002.out +DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_raw_flush.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_raw_flush.test new file mode 100644 index 00000000000..f95fc0137a2 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_raw_flush.test @@ -0,0 +1,45 @@ +# +# Purpose: +# When using mariadb-binlog with options for --raw and --stop-never, events +# from the master's currently active log file should be written to their +# respective log file specified by --result-file, and shown on-disk. This test +# ensures that the log files on disk, created by mariadb-binlog, have the most +# up-to-date events from the master. +# +# Methodology: +# On the master, rotate to a newly active binlog file and write an event to +# it. Read the master's binlog using mariadb-binlog with --raw and --stop-never +# and write the data to an intermediary binlog file (a timeout is used on this +# command to ensure it exits). Read the local intermediary binlog file to ensure +# that the master's most recent event exists in the local file. +# +# References: +# MDEV-14608: mysqlbinlog lastest backupfile size is 0 +# + +--source include/linux.inc +--source include/have_log_bin.inc + +# Create newly active log +CREATE TABLE t1 (a int); +FLUSH LOGS; +INSERT INTO t1 VALUES (1); + +# Read binlog data from master to intermediary result file +--let TIMEOUT=1 +--echo # timeout TIMEOUT MYSQL_BINLOG --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=MASTER_MYPORT --stop-never --result-file=MYSQLTEST_VARDIR/tmp/ master-bin.000001 +--error 124 # Error 124 means timeout was reached +--exec timeout $TIMEOUT $MYSQL_BINLOG --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --stop-never --result-file=$MYSQLTEST_VARDIR/tmp/ master-bin.000001 + +# Ensure the binlog output has the most recent events from the master +--echo # MYSQL_BINLOG MYSQLTEST_VARDIR/tmp/master-bin.000002 > MYSQLTEST_VARDIR/tmp/local-bin.000002.out +--exec $MYSQL_BINLOG $MYSQLTEST_VARDIR/tmp/master-bin.000002 > $MYSQLTEST_VARDIR/tmp/local-bin.000002.out +--let SEARCH_PATTERN= GTID 0-1-2 +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/local-bin.000002.out +--source include/search_pattern_in_file.inc + +# Cleanup +DROP TABLE t1; +--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000001 +--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000002 +--remove_file $MYSQLTEST_VARDIR/tmp/local-bin.000002.out From cd88b0831f7bea7e313af7d0bd96b7050f6c9d60 Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Mon, 15 Nov 2021 16:33:45 -0700 Subject: [PATCH 19/28] DBAAS-7828: Primary/replica: configuration change of autocommit=0 can not be applied Problem: ======== When the mysql.gtid_slave_pos table uses the InnoDB engine, and mysqld starts, it reads the table and begins a transaction. After reading the value, it should end the transaction and release all associated locks. The bug reported in DBAAS-7828 shows that when autocommit is off, the locks are not released, resulting in indefinite hangs on future attempts to change gtid_slave_pos. In particular, the transaction was not properly finalized because thd->server_status was not updated to reflect the end of the transaction. Solution: ======== This patch updates the code to properly commit the transaction after reading gtid_slave_pos during mysqld start-up. Reviewed By: ============ Andrei Elkin --- .../r/binlog_autocommit_off_no_hang.result | 6 +++ .../binlog_autocommit_off_no_hang-master.opt | 1 + .../t/binlog_autocommit_off_no_hang.test | 45 +++++++++++++++++++ sql/rpl_rli.cc | 2 +- 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/binlog/r/binlog_autocommit_off_no_hang.result create mode 100644 mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang-master.opt create mode 100644 mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang.test diff --git a/mysql-test/suite/binlog/r/binlog_autocommit_off_no_hang.result b/mysql-test/suite/binlog/r/binlog_autocommit_off_no_hang.result new file mode 100644 index 00000000000..71eecd881ca --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_autocommit_off_no_hang.result @@ -0,0 +1,6 @@ +ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb; +# Restart the server so mysqld reads the gtid_slave_pos using innodb +# Set gtid_slave_pos should not hang +SET GLOBAL gtid_slave_pos=@@gtid_binlog_pos; +COMMIT; +RESET MASTER; diff --git a/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang-master.opt b/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang-master.opt new file mode 100644 index 00000000000..e0fa81e6eeb --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang-master.opt @@ -0,0 +1 @@ +--autocommit=0 diff --git a/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang.test b/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang.test new file mode 100644 index 00000000000..8f1dbb2a2dd --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_autocommit_off_no_hang.test @@ -0,0 +1,45 @@ +# +# Purpose: +# When the mysql.gtid_slave_pos table uses the InnoDB engine, and mysqld +# starts, it reads the table and begins a transaction. After mysqld reads the +# value, it should end the transaction and release all associated locks. +# The bug reported in DBAAS-7828 shows that when autocommit is off, the locks +# are not released, resulting in indefinite hangs on future attempts to change +# gtid_slave_pos. This test ensures its fix such that the locks are properly +# released. +# +# References: +# DBAAS-7828: Primary/replica: configuration change of "autocommit=0" can +# not be applied +# + +--source include/have_innodb.inc +--source include/have_log_bin.inc + +# Reading gtid_slave_pos table is format independent so just use one for +# reduced test time +--source include/have_binlog_format_row.inc + +--let old_slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1) + +# Use a transactional engine +ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb; + +--echo # Restart the server so mysqld reads the gtid_slave_pos using innodb +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +--echo # Set gtid_slave_pos should not hang +SET GLOBAL gtid_slave_pos=@@gtid_binlog_pos; +COMMIT; + +# Revert table type +--disable_query_log +--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine +--enable_query_log + +RESET MASTER; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 88cb8fc5e1e..176801130d7 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1675,7 +1675,7 @@ end: { table->file->ha_index_or_rnd_end(); ha_commit_trans(thd, FALSE); - ha_commit_trans(thd, TRUE); + trans_commit(thd); } if (table_opened) { From 6437b304048d0b42e6b2b8f59631ea04bd3c2891 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 25 Mar 2022 07:05:08 +0400 Subject: [PATCH 20/28] MDEV-28166 sql_mode=ORACLE: fully qualified package function calls do not work: db.pkg.func() Also fixes MDEV-19328 sql_mode=ORACLE: Package function in VIEW --- .../suite/compat/oracle/r/sp-package.result | 210 +++++++++++++++++ .../suite/compat/oracle/t/sp-package.test | 213 ++++++++++++++++++ sql/item.cc | 20 +- sql/sql_class.h | 13 ++ sql/sql_lex.cc | 50 ++++ sql/sql_lex.h | 5 + sql/sql_yacc.yy | 5 + sql/sql_yacc_ora.yy | 5 + 8 files changed, 519 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/compat/oracle/r/sp-package.result b/mysql-test/suite/compat/oracle/r/sp-package.result index 598c766c808..a8be1a8eb16 100644 --- a/mysql-test/suite/compat/oracle/r/sp-package.result +++ b/mysql-test/suite/compat/oracle/r/sp-package.result @@ -2925,3 +2925,213 @@ END $$ CALL xyz.xyz123(17,18,@R); DROP PACKAGE xyz; DROP TABLE t1; +# +# MDEV-28166 sql_mode=ORACLE: fully qualified package function calls do not work: db.pkg.func() +# +SELECT `db `.pkg.func(); +ERROR 42000: Incorrect database name 'db ' +SELECT db.`pkg `.func(); +ERROR 42000: Incorrect routine name 'pkg ' +SELECT db.pkg.`func `(); +ERROR 42000: Incorrect routine name 'func ' +CREATE DATABASE db1; +USE db1; +CREATE PACKAGE pkg1 AS +FUNCTION f1 RETURN TEXT; +FUNCTION f2_db1_pkg1_f1 RETURN TEXT; +FUNCTION f2_pkg1_f1 RETURN TEXT; +FUNCTION f2_f1 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY pkg1 +AS +FUNCTION f1 RETURN TEXT IS +BEGIN +RETURN 'This is db1.pkg1.f1'; +END; +FUNCTION f2_db1_pkg1_f1 RETURN TEXT IS +BEGIN +RETURN db1.pkg1.f1(); +END; +FUNCTION f2_pkg1_f1 RETURN TEXT IS +BEGIN +RETURN pkg1.f1(); +END; +FUNCTION f2_f1 RETURN TEXT IS +BEGIN +RETURN f1(); +END; +END; +$$ +USE db1; +SELECT pkg1.f2_db1_pkg1_f1(); +pkg1.f2_db1_pkg1_f1() +This is db1.pkg1.f1 +SELECT pkg1.f2_pkg1_f1(); +pkg1.f2_pkg1_f1() +This is db1.pkg1.f1 +SELECT pkg1.f2_f1(); +pkg1.f2_f1() +This is db1.pkg1.f1 +SELECT db1.pkg1.f2_db1_pkg1_f1(); +db1.pkg1.f2_db1_pkg1_f1() +This is db1.pkg1.f1 +SELECT db1.pkg1.f2_pkg1_f1(); +db1.pkg1.f2_pkg1_f1() +This is db1.pkg1.f1 +SELECT db1.pkg1.f2_f1(); +db1.pkg1.f2_f1() +This is db1.pkg1.f1 +USE test; +SELECT db1.pkg1.f2_db1_pkg1_f1(); +db1.pkg1.f2_db1_pkg1_f1() +This is db1.pkg1.f1 +SELECT db1.pkg1.f2_pkg1_f1(); +db1.pkg1.f2_pkg1_f1() +This is db1.pkg1.f1 +SELECT db1.pkg1.f2_f1(); +db1.pkg1.f2_f1() +This is db1.pkg1.f1 +DROP DATABASE db1; +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE PACKAGE db1.pkg1 AS +FUNCTION f1 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY db1.pkg1 AS +FUNCTION f1 RETURN TEXT AS +BEGIN +RETURN 'This is db1.pkg1.f1'; +END; +END; +$$ +CREATE PACKAGE db2.pkg1 AS +FUNCTION f1 RETURN TEXT; +FUNCTION var1 RETURN TEXT; +FUNCTION var2 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY db2.pkg1 AS +m_var1 TEXT; +m_var2 TEXT; +FUNCTION f1 RETURN TEXT AS +BEGIN +RETURN 'This is db2.pkg1.f1'; +END; +FUNCTION var1 RETURN TEXT AS +BEGIN +RETURN m_var1; +END; +FUNCTION var2 RETURN TEXT AS +BEGIN +RETURN m_var2; +END; +BEGIN +m_var1:= db1.pkg1.f1(); +m_var2:= db2.pkg1.f1(); +END; +$$ +SELECT db2.pkg1.var1(), db2.pkg1.var2(); +db2.pkg1.var1() db2.pkg1.var2() +This is db1.pkg1.f1 This is db2.pkg1.f1 +DROP DATABASE db1; +DROP DATABASE db2; +CREATE PACKAGE pkg1 AS +FUNCTION f1(a TEXT) RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY pkg1 AS +FUNCTION f1(a TEXT) RETURN TEXT AS +BEGIN +RETURN a; +END; +END; +$$ +SELECT test.pkg1.f1('xxx'); +test.pkg1.f1('xxx') +xxx +SELECT test.pkg1.f1('xxx' AS a); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AS a)' at line 1 +DROP PACKAGE pkg1; +# +# MDEV-19328 sql_mode=ORACLE: Package function in VIEW +# +SET sql_mode=ORACLE; +CREATE PACKAGE test1 AS +FUNCTION f_test RETURN number; +END test1; +$$ +CREATE PACKAGE BODY test1 +AS +FUNCTION f_test RETURN NUMBER IS +BEGIN +RETURN 1; +END; +END test1; +$$ +SET sql_mode=ORACLE; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test(); +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE VIEW "v_test" AS select 1 AS "c1" from DUAL where 1 = "test"."test1"."f_test"() +character_set_client latin1 +collation_connection latin1_swedish_ci +SET sql_mode=DEFAULT; +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_test` AS select 1 AS `c1` from DUAL where 1 = `test`.`test1`.`f_test`() +character_set_client latin1 +collation_connection latin1_swedish_ci +DROP VIEW v_test; +SET sql_mode=DEFAULT; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test(); +ERROR 42000: FUNCTION test1.f_test does not exist +SET sql_mode=ORACLE; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test(); +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE VIEW "v_test" AS select 1 AS "c1" from DUAL where 1 = "test"."test1"."f_test"() +character_set_client latin1 +collation_connection latin1_swedish_ci +SET sql_mode=DEFAULT; +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_test` AS select 1 AS `c1` from DUAL where 1 = `test`.`test1`.`f_test`() +character_set_client latin1 +collation_connection latin1_swedish_ci +DROP VIEW v_test; +SET sql_mode=DEFAULT; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test(); +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_test` AS select 1 AS `c1` from DUAL where 1 = `test`.`test1`.`f_test`() +character_set_client latin1 +collation_connection latin1_swedish_ci +SET sql_mode=ORACLE; +SELECT * FROM v_test; +c1 +1 +SHOW CREATE VIEW v_test; +View v_test +Create View CREATE VIEW "v_test" AS select 1 AS "c1" from DUAL where 1 = "test"."test1"."f_test"() +character_set_client latin1 +collation_connection latin1_swedish_ci +DROP VIEW v_test; +SET sql_mode=ORACLE; +DROP PACKAGE test1; diff --git a/mysql-test/suite/compat/oracle/t/sp-package.test b/mysql-test/suite/compat/oracle/t/sp-package.test index 5eac3987982..8fcf72d3145 100644 --- a/mysql-test/suite/compat/oracle/t/sp-package.test +++ b/mysql-test/suite/compat/oracle/t/sp-package.test @@ -2682,3 +2682,216 @@ DELIMITER ;$$ CALL xyz.xyz123(17,18,@R); DROP PACKAGE xyz; DROP TABLE t1; + + +--echo # +--echo # MDEV-28166 sql_mode=ORACLE: fully qualified package function calls do not work: db.pkg.func() +--echo # + +--error ER_WRONG_DB_NAME +SELECT `db `.pkg.func(); +--error ER_SP_WRONG_NAME +SELECT db.`pkg `.func(); +--error ER_SP_WRONG_NAME +SELECT db.pkg.`func `(); + + +CREATE DATABASE db1; +USE db1; + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + FUNCTION f1 RETURN TEXT; + FUNCTION f2_db1_pkg1_f1 RETURN TEXT; + FUNCTION f2_pkg1_f1 RETURN TEXT; + FUNCTION f2_f1 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY pkg1 +AS + FUNCTION f1 RETURN TEXT IS + BEGIN + RETURN 'This is db1.pkg1.f1'; + END; + FUNCTION f2_db1_pkg1_f1 RETURN TEXT IS + BEGIN + RETURN db1.pkg1.f1(); + END; + FUNCTION f2_pkg1_f1 RETURN TEXT IS + BEGIN + RETURN pkg1.f1(); + END; + FUNCTION f2_f1 RETURN TEXT IS + BEGIN + RETURN f1(); + END; +END; +$$ +DELIMITER ;$$ + +USE db1; +SELECT pkg1.f2_db1_pkg1_f1(); +SELECT pkg1.f2_pkg1_f1(); +SELECT pkg1.f2_f1(); + +SELECT db1.pkg1.f2_db1_pkg1_f1(); +SELECT db1.pkg1.f2_pkg1_f1(); +SELECT db1.pkg1.f2_f1(); + +USE test; +SELECT db1.pkg1.f2_db1_pkg1_f1(); +SELECT db1.pkg1.f2_pkg1_f1(); +SELECT db1.pkg1.f2_f1(); + +DROP DATABASE db1; + + +# +# Testing db.pkg.func() in the package initialization section +# + +CREATE DATABASE db1; +CREATE DATABASE db2; + +DELIMITER $$; +CREATE PACKAGE db1.pkg1 AS + FUNCTION f1 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY db1.pkg1 AS + FUNCTION f1 RETURN TEXT AS + BEGIN + RETURN 'This is db1.pkg1.f1'; + END; +END; +$$ +DELIMITER ;$$ + + +DELIMITER $$; +CREATE PACKAGE db2.pkg1 AS + FUNCTION f1 RETURN TEXT; + FUNCTION var1 RETURN TEXT; + FUNCTION var2 RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY db2.pkg1 AS + m_var1 TEXT; + m_var2 TEXT; + FUNCTION f1 RETURN TEXT AS + BEGIN + RETURN 'This is db2.pkg1.f1'; + END; + FUNCTION var1 RETURN TEXT AS + BEGIN + RETURN m_var1; + END; + FUNCTION var2 RETURN TEXT AS + BEGIN + RETURN m_var2; + END; +BEGIN + m_var1:= db1.pkg1.f1(); + m_var2:= db2.pkg1.f1(); +END; +$$ +DELIMITER ;$$ + +SELECT db2.pkg1.var1(), db2.pkg1.var2(); + +DROP DATABASE db1; +DROP DATABASE db2; + +# +# Make sure fully qualified package function call does not support AS syntax: +# SELECT db.pkg.func(10 AS a); +# + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + FUNCTION f1(a TEXT) RETURN TEXT; +END; +$$ +CREATE PACKAGE BODY pkg1 AS + FUNCTION f1(a TEXT) RETURN TEXT AS + BEGIN + RETURN a; + END; +END; +$$ +DELIMITER ;$$ +SELECT test.pkg1.f1('xxx'); +--error ER_PARSE_ERROR +SELECT test.pkg1.f1('xxx' AS a); +DROP PACKAGE pkg1; + + +--echo # +--echo # MDEV-19328 sql_mode=ORACLE: Package function in VIEW +--echo # + +SET sql_mode=ORACLE; +DELIMITER $$; +CREATE PACKAGE test1 AS + FUNCTION f_test RETURN number; +END test1; +$$ +CREATE PACKAGE BODY test1 +AS + FUNCTION f_test RETURN NUMBER IS + BEGIN + RETURN 1; + END; +END test1; +$$ +DELIMITER ;$$ + + +SET sql_mode=ORACLE; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test(); +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +SET sql_mode=DEFAULT; +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +DROP VIEW v_test; + + +SET sql_mode=DEFAULT; +--error ER_SP_DOES_NOT_EXIST +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test(); + + +SET sql_mode=ORACLE; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test(); +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +SET sql_mode=DEFAULT; +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +DROP VIEW v_test; + + +SET sql_mode=DEFAULT; +CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test(); +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +SET sql_mode=ORACLE; +SELECT * FROM v_test; +--vertical_results +SHOW CREATE VIEW v_test; +--horizontal_results +DROP VIEW v_test; + +SET sql_mode=ORACLE; +DROP PACKAGE test1; diff --git a/sql/item.cc b/sql/item.cc index 2fe5411f972..f06055f0a08 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2898,9 +2898,11 @@ Item_sp::func_name(THD *thd) const /* Calculate length to avoid reallocation of string for sure */ size_t len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + m_name->m_name.length)*2 + //characters*quoting - 2 + // ` and ` + 2 + // quotes for the function name + 2 + // quotes for the package name (m_name->m_explicit_name ? 3 : 0) + // '`', '`' and '.' for the db + 1 + // '.' between package and function 1 + // end of string ALIGN_SIZE(1)); // to avoid String reallocation String qname((char *)alloc_root(thd->mem_root, len), len, @@ -2912,7 +2914,21 @@ Item_sp::func_name(THD *thd) const append_identifier(thd, &qname, &m_name->m_db); qname.append('.'); } - append_identifier(thd, &qname, &m_name->m_name); + if (m_sp && m_sp->m_handler == &sp_handler_package_function) + { + /* + In case of a package function split `pkg.func` and print + quoted `pkg` and `func` separately, so the entire result looks like: + `db`.`pkg`.`func` + */ + Database_qualified_name tmp= Database_qualified_name::split(m_name->m_name); + DBUG_ASSERT(tmp.m_db.length); + append_identifier(thd, &qname, &tmp.m_db); + qname.append('.'); + append_identifier(thd, &qname, &tmp.m_name); + } + else + append_identifier(thd, &qname, &m_name->m_name); return qname.c_ptr_safe(); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 311b47aea61..637e16c991b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6680,6 +6680,19 @@ public: } void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db, const LEX_CSTRING &name); + + static Database_qualified_name split(const LEX_CSTRING &txt) + { + DBUG_ASSERT(txt.str[txt.length] == '\0'); // Expect 0-terminated input + const char *dot= strchr(txt.str, '.'); + if (!dot) + return Database_qualified_name(NULL, 0, txt.str, txt.length); + size_t dblen= dot - txt.str; + Lex_cstring db(txt.str, dblen); + Lex_cstring name(txt.str + dblen + 1, txt.length - dblen - 1); + return Database_qualified_name(db, name); + } + // Export db and name as a qualified name string: 'db.name' size_t make_qname(char *dst, size_t dstlen) const { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d51b9bd5a26..cffc0eb25dd 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -8148,6 +8148,56 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb, } +/* + Create a 3-step qualified function call. + Currently it's possible for package routines only, e.g.: + SELECT db.pkg.func(); +*/ +Item *LEX::make_item_func_call_generic(THD *thd, + Lex_ident_cli_st *cdb, + Lex_ident_cli_st *cpkg, + Lex_ident_cli_st *cfunc, + List *args) +{ + static Lex_cstring dot(".", 1); + Lex_ident_sys db(thd, cdb), pkg(thd, cpkg), func(thd, cfunc); + Database_qualified_name q_db_pkg(db, pkg); + Database_qualified_name q_pkg_func(pkg, func); + sp_name *qname; + + if (db.is_null() || pkg.is_null() || func.is_null()) + return NULL; // EOM + + if (check_db_name((LEX_STRING*) static_cast(&db))) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db.str); + return NULL; + } + if (check_routine_name(&pkg) || + check_routine_name(&func)) + return NULL; + + // Concat `pkg` and `name` to `pkg.name` + LEX_CSTRING pkg_dot_func; + if (q_pkg_func.make_qname(thd->mem_root, &pkg_dot_func) || + check_ident_length(&pkg_dot_func) || + !(qname= new (thd->mem_root) sp_name(&db, &pkg_dot_func, true))) + return NULL; + + sp_handler_package_function.add_used_routine(thd->lex, thd, qname); + sp_handler_package_body.add_used_routine(thd->lex, thd, &q_db_pkg); + + thd->lex->safe_to_cache_query= 0; + + if (args && args->elements > 0) + return new (thd->mem_root) Item_func_sp(thd, thd->lex->current_context(), + qname, &sp_handler_package_function, + *args); + return new (thd->mem_root) Item_func_sp(thd, thd->lex->current_context(), + qname, &sp_handler_package_function); +} + + Item *LEX::create_item_qualified_asterisk(THD *thd, const Lex_ident_sys_st *name) { diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e1f34afa350..a63ec7c9153 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3697,6 +3697,11 @@ public: Item *make_item_func_substr(THD *thd, Item *a, Item *b); Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db, Lex_ident_cli_st *name, List *args); + Item *make_item_func_call_generic(THD *thd, + Lex_ident_cli_st *db, + Lex_ident_cli_st *pkg, + Lex_ident_cli_st *name, + List *args); my_var *create_outvar(THD *thd, const LEX_CSTRING *name); /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e0e4308ef5c..2852d2efc0c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11210,6 +11210,11 @@ function_call_generic: if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5)))) MYSQL_YYABORT; } + | ident_cli '.' ident_cli '.' ident_cli '(' opt_expr_list ')' + { + if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7)))) + MYSQL_YYABORT; + } ; fulltext_options: diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index b45cee01502..8b96937c955 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -11149,6 +11149,11 @@ function_call_generic: if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5)))) MYSQL_YYABORT; } + | ident_cli '.' ident_cli '.' ident_cli '(' opt_expr_list ')' + { + if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7)))) + MYSQL_YYABORT; + } ; fulltext_options: From 9b2fa2ae8e26e263714daa96d4b72dd6911994bd Mon Sep 17 00:00:00 2001 From: sjaakola Date: Fri, 11 Mar 2022 10:27:36 +0200 Subject: [PATCH 21/28] MDEV-24845 Oddities around innodb_fatal_semaphore_wait_threshold and global.innodb_disallow_writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a mtr test for reproducing a test scenario where despite of innodb_disallow_writes blocking, writes to file system can still happen. The test launches a garbd node, which triggers one of the cluster node to switch to SST donor state. In this state, all disk activity should be halted, and e.g. innodb_disallow_writes has been set. The test records md5sum aggregate over mariadb data directory when the node enters the donor state, and records another md5sum when the node leaves the donor state. If there is no IO activity in data directory, these hashes should be equal. For this test, the Donor state processing, has beeen instrumented so that, SST donor thread can be stopped when entering the donor state. The test uses this new dbug sync point, to control when to record the md5sums. New SST script was added: wsrep_sst_backup, and garbd uses backup method to lauch the donor node to call this script, and to enter in donor state. The backup script could be later extended as general purpose backup method for the cluster. This commit fixes also one race condition happening in wsrep_sst_rsync, like this: * wsrep_rsync_sst script requests for flush tables, and then waits in a loop until mariadbd has created file tables_flushed, as confirmation that FLUSH TABLES has completed * mariadbd's SST donor thread, wakes for the flush table request and then performs FTWRL, and after this it creates the tables_flushed file * note that SST script will now continue to startup rsync sending * mariadbd's SST donor thread now calls for sst_disallow_writes(), so that innodb would setup disk IO blockage, however rsyncing may already be ongoing at this point This race condition is fixed in this commit, by performing all disk IO blocking before creating the tables_flushed file. Reviewed-by: Jan Lindström --- .../r/galera_garbd_backup.result | 41 ++++++ .../galera_3nodes/t/galera_garbd_backup.cnf | 13 ++ .../galera_3nodes/t/galera_garbd_backup.test | 134 ++++++++++++++++++ scripts/CMakeLists.txt | 1 + scripts/wsrep_sst_backup.sh | 112 +++++++++++++++ sql/wsrep_sst.cc | 80 +++++++---- 6 files changed, 350 insertions(+), 31 deletions(-) create mode 100644 mysql-test/suite/galera_3nodes/r/galera_garbd_backup.result create mode 100644 mysql-test/suite/galera_3nodes/t/galera_garbd_backup.cnf create mode 100644 mysql-test/suite/galera_3nodes/t/galera_garbd_backup.test create mode 100644 scripts/wsrep_sst_backup.sh diff --git a/mysql-test/suite/galera_3nodes/r/galera_garbd_backup.result b/mysql-test/suite/galera_3nodes/r/galera_garbd_backup.result new file mode 100644 index 00000000000..f176ef1dd7f --- /dev/null +++ b/mysql-test/suite/galera_3nodes/r/galera_garbd_backup.result @@ -0,0 +1,41 @@ +connection node_1; +connection node_1; +connection node_2; +connection node_3; +connection node_1; +SET GLOBAL innodb_max_dirty_pages_pct=99; +SET GLOBAL innodb_max_dirty_pages_pct_lwm=99; +connection node_1; +CREATE TABLE t1 (f1 INTEGER, f2 varchar(1024)) Engine=InnoDB; +CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB; +INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +INSERT INTO t1 (f2) SELECT REPEAT('x', 1024) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4; +connection node_2; +Killing node #3 to free ports for garbd ... +connection node_3; +connection node_1; +SET GLOBAL debug_dbug = "+d,sync.wsrep_donor_state"; +Starting garbd ... +SET SESSION debug_sync = "now WAIT_FOR sync.wsrep_donor_state_reached"; +SET GLOBAL innodb_max_dirty_pages_pct_lwm=0; +SET GLOBAL innodb_max_dirty_pages_pct=0; +SET SESSION debug_sync = "now SIGNAL signal.wsrep_donor_state"; +SET GLOBAL debug_dbug = ""; +SET debug_sync='RESET'; +connection node_2; +Killing garbd ... +connection node_1; +connection node_2; +DROP TABLE t1; +DROP TABLE ten; +Restarting node #3 to satisfy MTR's end-of-test checks +connection node_3; +connection node_1; +SET GLOBAL innodb_max_dirty_pages_pct = 75.000000; +SET GLOBAL innodb_max_dirty_pages_pct_lwm = 0.000000; +connection node_1; +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); +connection node_2; +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); +connection node_3; +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); diff --git a/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.cnf b/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.cnf new file mode 100644 index 00000000000..8b7cb948a87 --- /dev/null +++ b/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.cnf @@ -0,0 +1,13 @@ +!include ../galera_3nodes.cnf + +[mysqld] +wsrep_sst_method=rsync + +[mysqld.1] +wsrep_node_name=node1 + +[mysqld.2] +wsrep_node_name=node2 + +[mysqld.3] +wsrep_node_name=node3 diff --git a/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.test b/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.test new file mode 100644 index 00000000000..302bf430dde --- /dev/null +++ b/mysql-test/suite/galera_3nodes/t/galera_garbd_backup.test @@ -0,0 +1,134 @@ +# +# A very basic test for the galera arbitrator. We shut down node #3 and use its port allocation to start garbd. +# As MTR does not allow multiple servers to be down at the same time, we are limited as to what we can test. +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_garbd.inc +--source include/big_test.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--connection node_1 +# Save original auto_increment_offset values. +--let $node_1=node_1 +--let $node_2=node_2 +--let $node_3=node_3 + +--let $galera_connection_name = node_3 +--let $galera_server_number = 3 +--source include/galera_connect.inc +--source suite/galera/include/galera_base_port.inc +--let $NODE_GALERAPORT_3 = $_NODE_GALERAPORT + +--source ../galera/include/auto_increment_offset_save.inc + +# Save galera ports +--connection node_1 +--source suite/galera/include/galera_base_port.inc +--let $NODE_GALERAPORT_1 = $_NODE_GALERAPORT +--let $datadir= `SELECT @@datadir` + +--let $innodb_max_dirty_pages_pct = `SELECT @@innodb_max_dirty_pages_pct` +--let $innodb_max_dirty_pages_pct_lwm = `SELECT @@innodb_max_dirty_pages_pct_lwm` + +SET GLOBAL innodb_max_dirty_pages_pct=99; +SET GLOBAL innodb_max_dirty_pages_pct_lwm=99; + +--connection node_1 +CREATE TABLE t1 (f1 INTEGER, f2 varchar(1024)) Engine=InnoDB; +CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB; +INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +INSERT INTO t1 (f2) SELECT REPEAT('x', 1024) FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4; + +--connection node_2 +--source suite/galera/include/galera_base_port.inc +--let $NODE_GALERAPORT_2 = $_NODE_GALERAPORT + +--echo Killing node #3 to free ports for garbd ... +--connection node_3 +--source include/shutdown_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +# stop SST donor thread when node is in donor state +SET GLOBAL debug_dbug = "+d,sync.wsrep_donor_state"; + +--echo Starting garbd ... +--exec $MTR_GARBD_EXE --address "gcomm://127.0.0.1:$NODE_GALERAPORT_1" --group my_wsrep_cluster --donor node1 --sst backup --options 'base_port=$NODE_GALERAPORT_3' > $MYSQL_TMP_DIR/garbd.log 2>&1 & + +SET SESSION debug_sync = "now WAIT_FOR sync.wsrep_donor_state_reached"; + +# +# get hash of data directory contents before BP dirty page flushing +# +--exec find $datadir -type f ! -name tables_flushed ! -name backup_sst_complete -exec md5sum {} \; | md5sum >$MYSQLTEST_VARDIR/tmp/innodb_before + +# this should force buffer pool flushing, if not already done by donor state change transfer +SET GLOBAL innodb_max_dirty_pages_pct_lwm=0; +SET GLOBAL innodb_max_dirty_pages_pct=0; + +--disable_query_log +--disable_result_log +select f1 from t1; +select * from ten; +--enable_result_log +--enable_query_log + +# +# +# record the hash of data directory contents after BP dirty page flushing +# +--exec find $datadir -type f ! -name tables_flushed ! -name backup_sst_complete -exec md5sum {} \; | md5sum >$MYSQLTEST_VARDIR/tmp/innodb_after + +# there should be no disk writes +--diff_files $MYSQLTEST_VARDIR/tmp/innodb_before $MYSQLTEST_VARDIR/tmp/innodb_after + +SET SESSION debug_sync = "now SIGNAL signal.wsrep_donor_state"; +SET GLOBAL debug_dbug = ""; +SET debug_sync='RESET'; + +--connection node_2 + +# +# garbd will die automatically, because of the backup SST script +# but just to be sure, sending explicit kill here, as well +# +--echo Killing garbd ... +# FreeBSD's /bin/pkill only supports short versions of the options: +# -o Select only the oldest (least recently started) +# -f Match against full argument lists +--error 0,1 +--exec pkill -o -f garbd.*$NODE_GALERAPORT_3 + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +--connection node_2 + +DROP TABLE t1; +DROP TABLE ten; + +--echo Restarting node #3 to satisfy MTR's end-of-test checks +--connection node_3 +let $restart_noprint=2; +--source include/start_mysqld.inc + +--connection node_1 +--eval SET GLOBAL innodb_max_dirty_pages_pct = $innodb_max_dirty_pages_pct +--eval SET GLOBAL innodb_max_dirty_pages_pct_lwm = $innodb_max_dirty_pages_pct_lwm + +--source ../galera/include/auto_increment_offset_restore.inc + +--connection node_1 +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); + +--connection node_2 +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); + +--connection node_3 +CALL mtr.add_suppression("WSREP: Protocol violation\. JOIN message sender 1\.0 \(.*\) is not in state transfer \(SYNCED\)"); diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 41b4e556835..bd8aac00012 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -275,6 +275,7 @@ ELSE() wsrep_sst_mysqldump wsrep_sst_rsync wsrep_sst_mariabackup + wsrep_sst_backup ) # The following script is sourced from other SST scripts, so it should # not be made executable. diff --git a/scripts/wsrep_sst_backup.sh b/scripts/wsrep_sst_backup.sh new file mode 100644 index 00000000000..55e11ddffc0 --- /dev/null +++ b/scripts/wsrep_sst_backup.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash + +set -ue + +# Copyright (C) 2017-2021 MariaDB +# Copyright (C) 2010-2014 Codership Oy +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston +# MA 02110-1335 USA. + +# This is a reference script for rsync-based state snapshot transfer + +RSYNC_REAL_PID=0 # rsync process id +STUNNEL_REAL_PID=0 # stunnel process id + +OS="$(uname)" +[ "$OS" = 'Darwin' ] && export -n LD_LIBRARY_PATH + +# Setting the path for lsof on CentOS +export PATH="/usr/sbin:/sbin:$PATH" + +. $(dirname "$0")/wsrep_sst_common + +MAGIC_FILE="$WSREP_SST_OPT_DATA/backup_sst_complete" +rm -rf "$MAGIC_FILE" + +WSREP_LOG_DIR=${WSREP_LOG_DIR:-""} +# if WSREP_LOG_DIR env. variable is not set, try to get it from my.cnf +if [ -z "$WSREP_LOG_DIR" ]; then + WSREP_LOG_DIR=$(parse_cnf mysqld innodb-log-group-home-dir '') +fi + +if [ -n "$WSREP_LOG_DIR" ]; then + # handle both relative and absolute paths + WSREP_LOG_DIR=$(cd $WSREP_SST_OPT_DATA; mkdir -p "$WSREP_LOG_DIR"; cd $WSREP_LOG_DIR; pwd -P) +else + # default to datadir + WSREP_LOG_DIR=$(cd $WSREP_SST_OPT_DATA; pwd -P) +fi + +if [ "$WSREP_SST_OPT_ROLE" = 'donor' ] +then + + [ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE" + + RC=0 + + if [ $WSREP_SST_OPT_BYPASS -eq 0 ]; then + + FLUSHED="$WSREP_SST_OPT_DATA/tables_flushed" + ERROR="$WSREP_SST_OPT_DATA/sst_error" + + [ -f "$FLUSHED" ] && rm -f "$FLUSHED" + [ -f "$ERROR" ] && rm -f "$ERROR" + + echo "flush tables" + + # Wait for : + # (a) Tables to be flushed, AND + # (b) Cluster state ID & wsrep_gtid_domain_id to be written to the file, OR + # (c) ERROR file, in case flush tables operation failed. + + while [ ! -r "$FLUSHED" ] && \ + ! grep -q -F ':' '--' "$FLUSHED" >/dev/null 2>&1 + do + # Check whether ERROR file exists. + if [ -f "$ERROR" ]; then + # Flush tables operation failed. + rm -f "$ERROR" + exit 255 + fi + sleep 0.2 + done + + STATE=$(cat "$FLUSHED") + rm -f "$FLUSHED" + + + else # BYPASS + + wsrep_log_info "Bypassing state dump." + fi + + echo 'continue' # now server can resume updating data + + echo "$STATE" > "$MAGIC_FILE" + + echo "done $STATE" + +elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' ] +then + wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'" + exit 22 # EINVAL + + +else + wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'" + exit 22 # EINVAL +fi + +exit 0 diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index dbebe91ec5b..0549d3e1c1d 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -1,4 +1,4 @@ -/* Copyright 2008-2020 Codership Oy +/* Copyright 2008-2022 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ #include "wsrep_xid.h" #include #include +#include "debug_sync.h" #include @@ -1415,6 +1416,33 @@ static int run_sql_command(THD *thd, const char *query) return 0; } +static void sst_disallow_writes (THD* thd, bool yes) +{ + char query_str[64]= { 0, }; + ssize_t const query_max= sizeof(query_str) - 1; + CHARSET_INFO *current_charset; + + current_charset= thd->variables.character_set_client; + + if (!is_supported_parser_charset(current_charset)) + { + /* Do not use non-supported parser character sets */ + WSREP_WARN("Current client character set is non-supported parser character set: %s", current_charset->csname); + thd->variables.character_set_client= &my_charset_latin1; + WSREP_WARN("For SST temporally setting character set to : %s", + my_charset_latin1.csname); + } + + snprintf (query_str, query_max, "SET GLOBAL innodb_disallow_writes=%d", + yes ? 1 : 0); + + if (run_sql_command(thd, query_str)) + { + WSREP_ERROR("Failed to disallow InnoDB writes"); + } + thd->variables.character_set_client= current_charset; +} + static int sst_flush_tables(THD* thd) { @@ -1477,6 +1505,10 @@ static int sst_flush_tables(THD* thd) { WSREP_INFO("Tables flushed."); + /* disable further disk IO */ + sst_disallow_writes(thd, true); + WSREP_INFO("Disabled further disk IO."); + /* Tables have been flushed. Create a file with cluster state ID and wsrep_gtid_domain_id. @@ -1485,39 +1517,14 @@ static int sst_flush_tables(THD* thd) snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid, (long long)wsrep_locked_seqno, wsrep_gtid_domain_id); err= sst_create_file(flush_success, content); + + if(err) + WSREP_INFO("Creating file for flush_success failed %d",err); } return err; } - -static void sst_disallow_writes (THD* thd, bool yes) -{ - char query_str[64] = { 0, }; - ssize_t const query_max = sizeof(query_str) - 1; - CHARSET_INFO *current_charset; - - current_charset = thd->variables.character_set_client; - - if (!is_supported_parser_charset(current_charset)) - { - /* Do not use non-supported parser character sets */ - WSREP_WARN("Current client character set is non-supported parser character set: %s", current_charset->csname); - thd->variables.character_set_client = &my_charset_latin1; - WSREP_WARN("For SST temporally setting character set to : %s", - my_charset_latin1.csname); - } - - snprintf (query_str, query_max, "SET GLOBAL innodb_disallow_writes=%d", - yes ? 1 : 0); - - if (run_sql_command(thd, query_str)) - { - WSREP_ERROR("Failed to disallow InnoDB writes"); - } - thd->variables.character_set_client = current_charset; -} - static void* sst_donor_thread (void* a) { sst_thread_arg* arg= (sst_thread_arg*)a; @@ -1565,8 +1572,7 @@ wait_signal: err= sst_flush_tables (thd.ptr); if (!err) { - sst_disallow_writes (thd.ptr, true); - /* + /* Lets also keep statements that modify binary logs (like RESET LOGS, RESET MASTER) from proceeding until the files have been transferred to the joiner node. @@ -1577,6 +1583,18 @@ wait_signal: } locked= true; + + WSREP_INFO("Donor state reached"); + + DBUG_EXECUTE_IF("sync.wsrep_donor_state", + { + const char act[]= + "now " + "SIGNAL sync.wsrep_donor_state_reached " + "WAIT_FOR signal.wsrep_donor_state"; + assert(!debug_sync_set_action(thd.ptr, + STRING_WITH_LEN(act))); + };); goto wait_signal; } } From 9f4ba624e2f7ad6cd35c842dbb07605f0751f4aa Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Mon, 24 May 2021 11:23:03 +0100 Subject: [PATCH 22/28] MDEV-24667 LOAD DATA INFILE on temporary table not written to slave binlog Problem: In regular replication, when master binlogged using statement format slave might not have written an event to its binary log when the Query event aimed at a temporary table. Specifically this was observed with LOAD DATA INFILE. This effect was possible because unlike master slave holds temporary tables in its pool and the master side check of existence of a temporary table at the format bin-logging decision did not apply. Solution: replace THD::has_thd_temporary_tables() with THD::has_temporary_tables which allows to identify temporary table presence on either side. -- Reviewed by Andrei Elkin. --- mysql-test/suite/rpl/r/mdev_24667.result | 30 +++++++++++++ mysql-test/suite/rpl/t/mdev_24667.cnf | 8 ++++ mysql-test/suite/rpl/t/mdev_24667.test | 56 ++++++++++++++++++++++++ sql/sql_class.h | 4 +- sql/temporary_tables.cc | 2 +- 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/rpl/r/mdev_24667.result create mode 100644 mysql-test/suite/rpl/t/mdev_24667.cnf create mode 100644 mysql-test/suite/rpl/t/mdev_24667.test diff --git a/mysql-test/suite/rpl/r/mdev_24667.result b/mysql-test/suite/rpl/r/mdev_24667.result new file mode 100644 index 00000000000..7c7342d63d6 --- /dev/null +++ b/mysql-test/suite/rpl/r/mdev_24667.result @@ -0,0 +1,30 @@ +include/rpl_init.inc [topology=1->2->3] +call mtr.add_suppression('Unsafe statement written to the binary log using '); +connection server_1; +set binlog_format=statement; +#first bug +create table t1 (a int); +create temporary table tmp like t1; +load data local infile 'MYSQLTEST_VARDIR/load_data' INTO TABLE tmp; +insert into t1 select * from tmp; +#second bug +create table t2 (a int); +create temporary table tmp2 like t2; +insert into tmp2 values(10); +update tmp2 set a = 20 limit 1; +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted +insert into t2 select * from tmp2; +connection server_2; +connection server_3; +#t1 should have 2 rows +select count(*) = 2 from t1; +count(*) = 2 +1 +#t2 should have 1 rows with a = 20 +select * from t2; +a +20 +connection server_1; +drop table t1, t2, tmp, tmp2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/mdev_24667.cnf b/mysql-test/suite/rpl/t/mdev_24667.cnf new file mode 100644 index 00000000000..58b605ad928 --- /dev/null +++ b/mysql-test/suite/rpl/t/mdev_24667.cnf @@ -0,0 +1,8 @@ +!include ../my.cnf + +[mysqld.3] +log-slave-updates + +[ENV] +SERVER_MYPORT_3= @mysqld.3.port +SERVER_MYSOCK_3= @mysqld.3.socket diff --git a/mysql-test/suite/rpl/t/mdev_24667.test b/mysql-test/suite/rpl/t/mdev_24667.test new file mode 100644 index 00000000000..d8490b335db --- /dev/null +++ b/mysql-test/suite/rpl/t/mdev_24667.test @@ -0,0 +1,56 @@ +# +# MDEV-24667 LOAD DATA INFILE/inserted rows not written to binlog +# +# In this test we will have a replication configuration like 1->2->3 +# 1 will have statement format +# 2 and 3 will have mixed format +# We will make some updates on temporary table which are unsafe , So 2 must +# Log these queries in row format, Since it is on tmp table , It wont be logged +# So the next query which copies the data from tmp table to normal must be logged +# into the row format. Instead of checking for the binlog We will compare the +# results on the 3, If no binlog is lost(ie it is logged into row format), There +# should not be any data loss. +--let $rpl_topology=1->2->3 +--source include/rpl_init.inc +--source include/have_binlog_format_mixed.inc +call mtr.add_suppression('Unsafe statement written to the binary log using '); +--connection server_1 + +set binlog_format=statement; +--echo #first bug +create table t1 (a int); +create temporary table tmp like t1; +--write_file $MYSQLTEST_VARDIR/load_data +1 +2 +EOF +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval load data local infile '$MYSQLTEST_VARDIR/load_data' INTO TABLE tmp; +insert into t1 select * from tmp; + +--echo #second bug +create table t2 (a int); +#insert into t2 values(10); +create temporary table tmp2 like t2; +insert into tmp2 values(10); +update tmp2 set a = 20 limit 1; +insert into t2 select * from tmp2; +--save_master_pos + +--connection server_2 +--sync_with_master +--save_master_pos + +--connection server_3 +--sync_with_master +--echo #t1 should have 2 rows +select count(*) = 2 from t1; +--echo #t2 should have 1 rows with a = 20 +select * from t2; + + +# cleanup +--connection server_1 +drop table t1, t2, tmp, tmp2; +--remove_file $MYSQLTEST_VARDIR/load_data +--source include/rpl_end.inc diff --git a/sql/sql_class.h b/sql/sql_class.h index a748def9b56..d3d54e11671 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3960,13 +3960,13 @@ public: */ DBUG_PRINT("debug", ("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s", - YESNO(has_thd_temporary_tables()), YESNO(in_sub_stmt), + YESNO(has_temporary_tables()), YESNO(in_sub_stmt), show_system_thread(system_thread))); if (in_sub_stmt == 0) { if (wsrep_binlog_format() == BINLOG_FORMAT_ROW) set_current_stmt_binlog_format_row(); - else if (!has_thd_temporary_tables()) + else if (!has_temporary_tables()) set_current_stmt_binlog_format_stmt(); } DBUG_VOID_RETURN; diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 005a520ff64..fb28ac40aa6 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -866,7 +866,7 @@ void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share) @return false Temporary tables exist true No temporary table exist */ -inline bool THD::has_temporary_tables() +bool THD::has_temporary_tables() { DBUG_ENTER("THD::has_temporary_tables"); bool result= (rgi_slave From fbcf0225e195bae2679272569e5a6310557ec853 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 25 Mar 2022 13:52:32 +0400 Subject: [PATCH 23/28] MDEV-19804 sql_mode=ORACLE: call procedure in packages Adding support for the fully qualified package procedure calls: BEGIN CALL db.pkg.proc(args); -- SQL/PSM call style db.pkg.proc(args); -- PL/SQL call style END; --- .../suite/compat/oracle/r/sp-package.result | 98 +++++++++++++++ .../suite/compat/oracle/t/sp-package.test | 114 ++++++++++++++++++ sql/sql_lex.cc | 34 ++++++ sql/sql_lex.h | 3 + sql/sql_yacc.yy | 24 +++- sql/sql_yacc_ora.yy | 42 ++++++- 6 files changed, 311 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/compat/oracle/r/sp-package.result b/mysql-test/suite/compat/oracle/r/sp-package.result index a8be1a8eb16..20e3d43fb85 100644 --- a/mysql-test/suite/compat/oracle/r/sp-package.result +++ b/mysql-test/suite/compat/oracle/r/sp-package.result @@ -3135,3 +3135,101 @@ collation_connection latin1_swedish_ci DROP VIEW v_test; SET sql_mode=ORACLE; DROP PACKAGE test1; +# +# MDEV-19804 sql_mode=ORACLE: call procedure in packages +# +CALL `db1 `.pkg.p; +ERROR 42000: Incorrect database name 'db1 ' +CALL db1.`pkg `.p; +ERROR 42000: Incorrect routine name 'pkg ' +CALL db1.pkg.`p `; +ERROR 42000: Incorrect routine name 'p ' +SET sql_mode=ORACLE; +CREATE PACKAGE pkg1 as +PROCEDURE p1(); +END; +$$ +CREATE PACKAGE BODY pkg1 as +PROCEDURE p1() as +BEGIN +SELECT 'test-function' AS c1; +END; +END; +$$ +CALL pkg1.p1; +c1 +test-function +CALL test.pkg1.p1; +c1 +test-function +SET sql_mode=DEFAULT; +CALL test.pkg1.p1; +c1 +test-function +SET sql_mode=ORACLE; +BEGIN +CALL pkg1.p1; +CALL test.pkg1.p1; +END +$$ +c1 +test-function +c1 +test-function +BEGIN +pkg1.p1; +test.pkg1.p1; +END +$$ +c1 +test-function +c1 +test-function +DROP PACKAGE pkg1; +CREATE DATABASE db1; +CREATE PACKAGE db1.pkg1 AS +PROCEDURE p1(a OUT TEXT); +END; +$$ +CREATE PACKAGE BODY db1.pkg1 AS +PROCEDURE p1(a OUT TEXT) AS +BEGIN +a:= 'This is db1.pkg1.p1'; +END; +END; +$$ +CREATE DATABASE db2; +CREATE PACKAGE db2.pkg1 AS +FUNCTION var1 RETURN TEXT; +PROCEDURE p1(a OUT TEXT); +PROCEDURE p2_db1_pkg1_p1; +END; +$$ +CREATE PACKAGE BODY db2.pkg1 AS +m_var1 TEXT; +FUNCTION var1 RETURN TEXT AS +BEGIN +RETURN m_var1; +END; +PROCEDURE p1(a OUT TEXT) AS +BEGIN +a:= 'This is db2.pkg1.p1'; +END; +PROCEDURE p2_db1_pkg1_p1 AS +a TEXT; +BEGIN +db1.pkg1.p1(a); +SELECT a; +END; +BEGIN +db1.pkg1.p1(m_var1); +END; +$$ +SELECT db2.pkg1.var1(); +db2.pkg1.var1() +This is db1.pkg1.p1 +CALL db2.pkg1.p2_db1_pkg1_p1; +a +This is db1.pkg1.p1 +DROP DATABASE db1; +DROP DATABASE db2; diff --git a/mysql-test/suite/compat/oracle/t/sp-package.test b/mysql-test/suite/compat/oracle/t/sp-package.test index 8fcf72d3145..578ee4e0073 100644 --- a/mysql-test/suite/compat/oracle/t/sp-package.test +++ b/mysql-test/suite/compat/oracle/t/sp-package.test @@ -2895,3 +2895,117 @@ DROP VIEW v_test; SET sql_mode=ORACLE; DROP PACKAGE test1; + + +--echo # +--echo # MDEV-19804 sql_mode=ORACLE: call procedure in packages +--echo # + +--error ER_WRONG_DB_NAME +CALL `db1 `.pkg.p; +--error ER_SP_WRONG_NAME +CALL db1.`pkg `.p; +--error ER_SP_WRONG_NAME +CALL db1.pkg.`p `; + + +SET sql_mode=ORACLE; +DELIMITER $$; +CREATE PACKAGE pkg1 as + PROCEDURE p1(); +END; +$$ +CREATE PACKAGE BODY pkg1 as + PROCEDURE p1() as + BEGIN + SELECT 'test-function' AS c1; + END; +END; +$$ +DELIMITER ;$$ + +CALL pkg1.p1; +CALL test.pkg1.p1; + +# In sql_mode=DEFAULT we support fully qualified package function names +# (this is needed for VIEWs). Let's make sure we also support fully +# qualified package procedure names, for symmetry + +SET sql_mode=DEFAULT; +CALL test.pkg1.p1; +SET sql_mode=ORACLE; + +DELIMITER $$; +BEGIN + CALL pkg1.p1; + CALL test.pkg1.p1; +END +$$ +DELIMITER ;$$ + +DELIMITER $$; +BEGIN + pkg1.p1; + test.pkg1.p1; +END +$$ +DELIMITER ;$$ + +DROP PACKAGE pkg1; + + +# +# Testing packages in different databases calling each other +# in routines and in the initialization section. +# + +CREATE DATABASE db1; +DELIMITER $$; +CREATE PACKAGE db1.pkg1 AS + PROCEDURE p1(a OUT TEXT); +END; +$$ +CREATE PACKAGE BODY db1.pkg1 AS + PROCEDURE p1(a OUT TEXT) AS + BEGIN + a:= 'This is db1.pkg1.p1'; + END; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE db2; +DELIMITER $$; +CREATE PACKAGE db2.pkg1 AS + FUNCTION var1 RETURN TEXT; + PROCEDURE p1(a OUT TEXT); + PROCEDURE p2_db1_pkg1_p1; +END; +$$ +CREATE PACKAGE BODY db2.pkg1 AS + m_var1 TEXT; + FUNCTION var1 RETURN TEXT AS + BEGIN + RETURN m_var1; + END; + PROCEDURE p1(a OUT TEXT) AS + BEGIN + a:= 'This is db2.pkg1.p1'; + END; + PROCEDURE p2_db1_pkg1_p1 AS + a TEXT; + BEGIN + db1.pkg1.p1(a); + SELECT a; + END; +BEGIN + db1.pkg1.p1(m_var1); +END; +$$ +DELIMITER ;$$ + +SELECT db2.pkg1.var1(); +CALL db2.pkg1.p2_db1_pkg1_p1; + +DROP DATABASE db1; +DROP DATABASE db2; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index cffc0eb25dd..c70fef9709f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -7893,6 +7893,40 @@ bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1, } +bool LEX::call_statement_start(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &pkg, + const LEX_CSTRING &proc) +{ + Database_qualified_name q_db_pkg(db, pkg); + Database_qualified_name q_pkg_proc(pkg, proc); + sp_name *spname; + + sql_command= SQLCOM_CALL; + + if (check_db_name((LEX_STRING*) const_cast(&db))) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db.str); + return NULL; + } + if (check_routine_name(&pkg) || + check_routine_name(&proc)) + return NULL; + + // Concat `pkg` and `name` to `pkg.name` + LEX_CSTRING pkg_dot_proc; + if (q_pkg_proc.make_qname(thd->mem_root, &pkg_dot_proc) || + check_ident_length(&pkg_dot_proc) || + !(spname= new (thd->mem_root) sp_name(&db, &pkg_dot_proc, true))) + return NULL; + + sp_handler_package_function.add_used_routine(thd->lex, thd, spname); + sp_handler_package_body.add_used_routine(thd->lex, thd, &q_db_pkg); + + return !(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(spname, + &sp_handler_package_procedure)); +} + + sp_package *LEX::get_sp_package() const { return sphead ? sphead->get_package() : NULL; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a63ec7c9153..b5cc9604a8f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3478,6 +3478,9 @@ public: bool call_statement_start(THD *thd, const LEX_CSTRING *name); bool call_statement_start(THD *thd, const LEX_CSTRING *name1, const LEX_CSTRING *name2); + bool call_statement_start(THD *thd, const LEX_CSTRING &name1, + const LEX_CSTRING &name2, + const LEX_CSTRING &name3); sp_variable *find_variable(const LEX_CSTRING *name, sp_pcontext **ctx, const Sp_rcontext_handler **rh) const; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2852d2efc0c..a427c7a40c5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3345,9 +3345,29 @@ sp_suid: ; call: - CALL_SYM sp_name + CALL_SYM ident { - if (unlikely(Lex->call_statement_start(thd, $2))) + if (unlikely(Lex->call_statement_start(thd, &$2))) + MYSQL_YYABORT; + } + opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } + | CALL_SYM ident '.' ident + { + if (unlikely(Lex->call_statement_start(thd, &$2, &$4))) + MYSQL_YYABORT; + } + opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } + | CALL_SYM ident '.' ident '.' ident + { + if (unlikely(Lex->call_statement_start(thd, $2, $4, $6))) MYSQL_YYABORT; } opt_sp_cparam_list diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 8b96937c955..f0e6b5b54c7 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -2999,9 +2999,29 @@ sp_suid: ; call: - CALL_SYM sp_name + CALL_SYM ident { - if (unlikely(Lex->call_statement_start(thd, $2))) + if (unlikely(Lex->call_statement_start(thd, &$2))) + MYSQL_YYABORT; + } + opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } + | CALL_SYM ident '.' ident + { + if (unlikely(Lex->call_statement_start(thd, &$2, &$4))) + MYSQL_YYABORT; + } + opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } + | CALL_SYM ident '.' ident '.' ident + { + if (unlikely(Lex->call_statement_start(thd, $2, $4, $6))) MYSQL_YYABORT; } opt_sp_cparam_list @@ -3922,12 +3942,30 @@ sp_statement: MYSQL_YYABORT; } opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } | ident_directly_assignable '.' ident { if (unlikely(Lex->call_statement_start(thd, &$1, &$3))) MYSQL_YYABORT; } opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } + | ident_directly_assignable '.' ident '.' ident + { + if (unlikely(Lex->call_statement_start(thd, $1, $3, $5))) + MYSQL_YYABORT; + } + opt_sp_cparam_list + { + if (Lex->check_cte_dependencies_and_resolve_references()) + MYSQL_YYABORT; + } ; sp_proc_stmt_statement: From 549a71e74b2fa494efcd79635a5db8af0d541f99 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Fri, 25 Mar 2022 18:29:39 +0530 Subject: [PATCH 24/28] MDEV-21873: 10.2 to 10.3 upgrade doesn't remove semi-sync reference from mysql.plugin table Fix: Since mysql_upgrade runs commands from mysql_system_tables.fix, added sql commands to check for semisync plugins in INFORMATION_SCHEMA.PLUGINS and if they aren't there then delete them from mysql.plugin. --- scripts/mysql_system_tables_fix.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index f87f1aa66f4..79866d149bb 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -796,3 +796,10 @@ ALTER TABLE help_topic MODIFY url TEXT NOT NULL; # MDEV-7383 - varbinary on mix/max of column_stats alter table column_stats modify min_value varbinary(255) DEFAULT NULL, modify max_value varbinary(255) DEFAULT NULL; + +# MDEV-21873: 10.2 to 10.3 upgrade doesn't remove semi-sync reference from +# mysql.plugin table. +# As per suggested fix, check INFORMATION_SCHEMA.PLUGINS +# and if semisync plugins aren't there, delete them from mysql.plugin. +DELETE FROM mysql.plugin WHERE name="rpl_semi_sync_master" AND NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME="rpl_semi_sync_master"); +DELETE FROM mysql.plugin WHERE name="rpl_semi_sync_slave" AND NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME="rpl_semi_sync_slave"); From e048289e557315b068a15083267329c443faadd3 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 25 Mar 2022 11:04:56 -0700 Subject: [PATCH 25/28] MDEV-27937 Assertion failure when executing prepared statement with ? in IN list This bug affected queries with IN predicates that contain parameter markers in the value list. Such queries are executed via prepared statements. The problem appeared only if the number of elements in the value list was greater than the set value of the system variable in_predicate_conversion_threshold. The patch unconditionally prohibits conversion of an IN predicate to the equivalent IN predicand if the value list of the IN predicate contains parameters markers. Approved by Oleksandr Byelkin --- mysql-test/main/opt_tvc.result | 27 +++++++++++++++++++++++++++ mysql-test/main/opt_tvc.test | 26 ++++++++++++++++++++++++++ sql/item_cmpfunc.cc | 7 ++++--- sql/item_cmpfunc.h | 2 ++ sql/sql_tvc.cc | 27 ++++++++++++++++++++++++--- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/mysql-test/main/opt_tvc.result b/mysql-test/main/opt_tvc.result index a68e70e8a25..02d9096ed09 100644 --- a/mysql-test/main/opt_tvc.result +++ b/mysql-test/main/opt_tvc.result @@ -732,3 +732,30 @@ a b 4 4 drop table t1; SET @@in_predicate_conversion_threshold= default; +# +# MDEV-27937: Prepared statement with ? in the list if IN predicate +# +set in_predicate_conversion_threshold=2; +create table t1 (id int, a int, b int); +insert into t1 values (1,3,30), (2,7,70), (3,1,10); +prepare stmt from " +select * from t1 where a in (7, ?, 5, 1); +"; +execute stmt using 3; +id a b +1 3 30 +2 7 70 +3 1 10 +deallocate prepare stmt; +prepare stmt from " +select * from t1 where (a,b) in ((7,70), (3,?), (5,50), (1,10)); +"; +execute stmt using 30; +id a b +1 3 30 +2 7 70 +3 1 10 +deallocate prepare stmt; +drop table t1; +set in_predicate_conversion_threshold=default; +# End of 10.3 tests diff --git a/mysql-test/main/opt_tvc.test b/mysql-test/main/opt_tvc.test index e4e8c6d7919..f8469f22aa1 100644 --- a/mysql-test/main/opt_tvc.test +++ b/mysql-test/main/opt_tvc.test @@ -428,3 +428,29 @@ eval $query; drop table t1; SET @@in_predicate_conversion_threshold= default; +--echo # +--echo # MDEV-27937: Prepared statement with ? in the list if IN predicate +--echo # + +set in_predicate_conversion_threshold=2; + +create table t1 (id int, a int, b int); +insert into t1 values (1,3,30), (2,7,70), (3,1,10); + +prepare stmt from " +select * from t1 where a in (7, ?, 5, 1); +"; +execute stmt using 3; +deallocate prepare stmt; + +prepare stmt from " +select * from t1 where (a,b) in ((7,70), (3,?), (5,50), (1,10)); +"; +execute stmt using 30; +deallocate prepare stmt; + +drop table t1; + +set in_predicate_conversion_threshold=default; + +--echo # End of 10.3 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 38f0a285e84..f41414f8ae9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4472,10 +4472,11 @@ void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding) Query_arena *arena, backup; arena= thd->activate_stmt_arena_if_needed(&backup); - if (to_be_transformed_into_in_subq(thd)) + if (!transform_into_subq_checked) { - transform_into_subq= true; - thd->lex->current_select->in_funcs.push_back(this, thd->mem_root); + if ((transform_into_subq= to_be_transformed_into_in_subq(thd))) + thd->lex->current_select->in_funcs.push_back(this, thd->mem_root); + transform_into_subq_checked= true; } if (arena) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 4c88f5b274f..f3d3be44b62 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2299,6 +2299,7 @@ protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value); bool transform_into_subq; + bool transform_into_subq_checked; public: /// An array of values, created when the bisection lookup method is used in_vector *array; @@ -2321,6 +2322,7 @@ public: Item_func_opt_neg(thd, list), Predicant_to_list_comparator(thd, arg_count - 1), transform_into_subq(false), + transform_into_subq_checked(false), array(0), have_null(0), arg_types_compatible(FALSE), emb_on_expr_nest(0) { } diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 3866b7c9352..13efd973326 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -900,8 +900,6 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd, if (!transform_into_subq) return this; - transform_into_subq= false; - List values; LEX *lex= thd->lex; @@ -1058,15 +1056,38 @@ uint32 Item_func_in::max_length_of_left_expr() bool Item_func_in::to_be_transformed_into_in_subq(THD *thd) { + bool is_row_list= args[1]->type() == Item::ROW_ITEM; uint values_count= arg_count-1; - if (args[1]->type() == Item::ROW_ITEM) + if (is_row_list) values_count*= ((Item_row *)(args[1]))->cols(); if (thd->variables.in_subquery_conversion_threshold == 0 || thd->variables.in_subquery_conversion_threshold > values_count) return false; + if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) + return true; + + /* Occurence of '?' in IN list is checked only for PREPARE commands */ + for (uint i=1; i < arg_count; i++) + { + if (!is_row_list) + { + if (args[i]->type() == Item::PARAM_ITEM) + return false; + } + else + { + Item_row *row_list= (Item_row *)(args[i]); + for (uint j=0; j < row_list->cols(); j++) + { + if (row_list->element_index(j)->type() == Item::PARAM_ITEM) + return false; + } + } + } + return true; } From 7af133cc111c0fa8f5afa99055d2a22eaac6d94e Mon Sep 17 00:00:00 2001 From: hongdongjian Date: Fri, 25 Mar 2022 19:47:40 +0800 Subject: [PATCH 26/28] MDEV-28177: server_audit; Update the offset of dbName on the aarch64 platform. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the aarch64 platform, MySQL 5.7.33 cannot install this version of the audit plugin, but X86_64 can run well。 --- plugin/server_audit/server_audit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 14f5b2f98b4..81ec33973e3 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -2328,6 +2328,9 @@ int get_db_mysql57(MYSQL_THD thd, char **name, int *len) #ifdef __x86_64__ db_off= 608; db_len_off= 616; +#elif __aarch64__ + db_off= 632; + db_len_off= 640; #else db_off= 0; db_len_off= 0; @@ -2338,6 +2341,9 @@ int get_db_mysql57(MYSQL_THD thd, char **name, int *len) #ifdef __x86_64__ db_off= 536; db_len_off= 544; +#elif __aarch64__ + db_off= 552; + db_len_off= 560; #else db_off= 0; db_len_off= 0; From 303448bc912486f4766129cc407a5077a3ca4359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 28 Mar 2022 13:36:36 +0300 Subject: [PATCH 27/28] MDEV-27931: buf_page_is_corrupted() wrongly claims corruption In commit 437da7bc54daa131b46900128ebe3ad2ca25c11a (MDEV-19534), the default value of the global variable srv_checksum_algorithm in innochecksum was changed from SRV_CHECKSUM_ALGORITHM_INNODB to implied 0 (innodb_checksum_algorithm=crc32). As a result, the function buf_page_is_corrupted() would by default invoke buf_calc_page_crc32() in innochecksum, and crc32_inited would hold. This would cause "innochecksum" to fail on a particular page. The actual problem is older, introduced in 2011 in mysql/mysql-server@17e497bdb793bc6b8360aa1c626dcd8bb5cfad1b (MySQL 5.6.3). It should affect the validation of pages of old data files that were written with innodb_checksum_algorithm=innodb. When using innodb_checksum_algorithm=crc32 (the default setting since MariaDB Server 10.2), some valid pages would be rejected only because exactly one of the two checksum fields accidentally matches the innodb_checksum_algorithm=crc32 value. buf_page_is_corrupted(): Simplify the logic of non-strict checksum validation, by always invoking buf_calc_page_crc32(). Remove a bogus condition that if only one of the checksum fields contains the value returned by buf_calc_page_crc32(), the page is corrupted. --- storage/innobase/buf/buf0buf.cc | 94 ++++++++------------------------- 1 file changed, 23 insertions(+), 71 deletions(-) diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 505539f0217..679f12ca6d1 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -977,8 +977,6 @@ buf_page_is_corrupted( #endif size_t checksum_field1 = 0; size_t checksum_field2 = 0; - uint32_t crc32 = 0; - bool crc32_inited = false; ulint page_type = mach_read_from_2(read_buf + FIL_PAGE_TYPE); @@ -1104,8 +1102,13 @@ buf_page_is_corrupted( case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: return !buf_page_is_checksum_valid_none( read_buf, checksum_field1, checksum_field2); + case SRV_CHECKSUM_ALGORITHM_NONE: + /* should have returned false earlier */ + break; case SRV_CHECKSUM_ALGORITHM_CRC32: case SRV_CHECKSUM_ALGORITHM_INNODB: + const uint32_t crc32 = buf_calc_page_crc32(read_buf); + if (buf_page_is_checksum_valid_none(read_buf, checksum_field1, checksum_field2)) { #ifdef UNIV_INNOCHECKSUM @@ -1121,7 +1124,7 @@ buf_page_is_corrupted( " crc32 = " UINT32PF "; recorded = " ULINTPF ";\n", cur_page_num, buf_calc_page_new_checksum(read_buf), - buf_calc_page_crc32(read_buf), + crc32, checksum_field1); } #endif /* UNIV_INNOCHECKSUM */ @@ -1138,84 +1141,33 @@ buf_page_is_corrupted( != mach_read_from_4(read_buf + FIL_PAGE_LSN) && checksum_field2 != BUF_NO_CHECKSUM_MAGIC) { - if (curr_algo == SRV_CHECKSUM_ALGORITHM_CRC32) { - DBUG_EXECUTE_IF( - "page_intermittent_checksum_mismatch", { - static int page_counter; - if (page_counter++ == 2) { - checksum_field2++; - } - }); + DBUG_EXECUTE_IF( + "page_intermittent_checksum_mismatch", { + static int page_counter; + if (page_counter++ == 2) return true; + }); - crc32 = buf_page_check_crc32(read_buf, - checksum_field2); - crc32_inited = true; - - if (checksum_field2 != crc32 - && checksum_field2 - != buf_calc_page_old_checksum(read_buf)) { - return true; - } - } else { - ut_ad(curr_algo - == SRV_CHECKSUM_ALGORITHM_INNODB); - - if (checksum_field2 - != buf_calc_page_old_checksum(read_buf)) { - crc32 = buf_page_check_crc32( - read_buf, checksum_field2); - crc32_inited = true; - - if (checksum_field2 != crc32) { - return true; - } - } + if ((checksum_field1 != crc32 + || checksum_field2 != crc32) + && checksum_field2 + != buf_calc_page_old_checksum(read_buf)) { + return true; } } - if (checksum_field1 == 0 - || checksum_field1 == BUF_NO_CHECKSUM_MAGIC) { - } else if (curr_algo == SRV_CHECKSUM_ALGORITHM_CRC32) { - if (!crc32_inited) { - crc32 = buf_page_check_crc32( - read_buf, checksum_field2); - crc32_inited = true; - } - - if (checksum_field1 != crc32 + switch (checksum_field1) { + case 0: + case BUF_NO_CHECKSUM_MAGIC: + break; + default: + if ((checksum_field1 != crc32 + || checksum_field2 != crc32) && checksum_field1 != buf_calc_page_new_checksum(read_buf)) { return true; } - } else { - ut_ad(curr_algo == SRV_CHECKSUM_ALGORITHM_INNODB); - - if (checksum_field1 - != buf_calc_page_new_checksum(read_buf)) { - - if (!crc32_inited) { - crc32 = buf_page_check_crc32( - read_buf, checksum_field2); - crc32_inited = true; - } - - if (checksum_field1 != crc32) { - return true; - } - } } - if (crc32_inited - && ((checksum_field1 == crc32 - && checksum_field2 != crc32) - || (checksum_field1 != crc32 - && checksum_field2 == crc32))) { - return true; - } - - break; - case SRV_CHECKSUM_ALGORITHM_NONE: - /* should have returned false earlier */ break; } From 739002eec90efa73a3b77db1cc46b313e8ac1bfd Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 28 Mar 2022 09:42:26 +0200 Subject: [PATCH 28/28] MDEV-28178 Windows : sporadic ER_ERROR_ON_RENAME .. (errno: 13 "Permission denied") On affected machine, the error happens sporadically in innodb.instant_alter_limit. Procmon shows SetRenameInformationFile failing with ERROR_ACCESS_DENIED. In this case, the destination file was previously opened rsp oplocked by Windows defender antivirus. The fix is to retry MoveFileEx on ERROR_ACCESS_DENIED. --- mysys/my_rename.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mysys/my_rename.c b/mysys/my_rename.c index 5702af94272..23dbec2d7ff 100644 --- a/mysys/my_rename.c +++ b/mysys/my_rename.c @@ -46,12 +46,15 @@ static BOOL win_rename_with_retries(const char *from, const char *to) for (int retry= RENAME_MAX_RETRIES; retry--;) { - DWORD ret = MoveFileEx(from, to, + BOOL ret= MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); - DBUG_ASSERT(fp == NULL || (ret == FALSE && GetLastError() == ERROR_SHARING_VIOLATION)); + if (ret) + return ret; - if (!ret && (GetLastError() == ERROR_SHARING_VIOLATION)) + DWORD last_error= GetLastError(); + if (last_error == ERROR_SHARING_VIOLATION || + last_error == ERROR_ACCESS_DENIED) { #ifndef DBUG_OFF /*