From fd1979bc9a535735ed3f3a7dbb67d09568dd8417 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 9 Dec 2019 01:17:16 +0400 Subject: MDEV-18463 Don't allow multiple table CONSTRAINTs with the same name. Add necessary checks. --- sql/sql_table.cc | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4ad68d9d03b..878c09286b5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4196,10 +4196,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, continue; { - /* Check that there's no repeating constraint names. */ + /* Check that there's no repeating table CHECK constraint names. */ List_iterator_fast dup_it(alter_info->check_constraint_list); - Virtual_column_info *dup_check; + const Virtual_column_info *dup_check; while ((dup_check= dup_it++) && dup_check != check) { if (check->name.length == dup_check->name.length && @@ -4212,6 +4212,27 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + /* Check that there's no repeating key constraint names. */ + List_iterator_fast key_it(alter_info->key_list); + while (const Key *key= key_it++) + { + /* + Not all keys considered to be the CONSTRAINT + Noly Primary Key UNIQUE and Foreign keys. + */ + if (key->type != Key::PRIMARY && key->type != Key::UNIQUE && + key->type != Key::FOREIGN_KEY) + continue; + + if (check->name.length == key->name.length && + my_strcasecmp(system_charset_info, + check->name.str, key->name.str) == 0) + { + my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str); + DBUG_RETURN(TRUE); + } + } + if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN, system_charset_info, 1)) { @@ -8176,6 +8197,35 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } } + + if (!alter_info->check_constraint_list.is_empty()) + { + /* Check the table FOREIGN KEYs for name duplications. */ + List fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + List_iterator fk_key_it(fk_child_key_list); + while ((f_key= fk_key_it++)) + { + List_iterator_fast + c_it(alter_info->check_constraint_list); + Virtual_column_info *check; + while ((check= c_it++)) + { + if (!check->name.length || check->automatic_name) + continue; + + if (check->name.length == f_key->foreign_id->length && + my_strcasecmp(system_charset_info, f_key->foreign_id->str, + check->name.str) == 0) + { + my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str); + goto err; + } + } + } + } + /* Add new constraints */ new_constraint_list.append(&alter_info->check_constraint_list); -- cgit v1.2.1 From 59e14b96847e458909ca7fcf95b144fd6ccdb708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Mon, 9 Dec 2019 08:09:56 +0200 Subject: MDEV-21189: Dropping partition with 'wsrep_OSU_method=RSU' and 'SESSION sql_log_bin = 0' cases the galera node to hang Found two bugs (1) have_committing_connections was missing mutex unlock on one exit case. As this function is called on a loop it caused mutex lock when we already owned the mutex. This could cause hang. (2) wsrep_RSU_begin did set up error code when partition to be dropped could not be MDL-locked because of concurrent operations but wrong error code was propagated to upper layer causing error to be ignored. This could have also caused the hang. --- sql/wsrep_mysqld.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 062c9dcd74c..cfcc04bff2c 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1784,7 +1784,7 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_) } my_error(ER_LOCK_DEADLOCK, MYF(0)); - return(1); + return(-1); } wsrep_seqno_t seqno = wsrep->pause(wsrep); @@ -2311,6 +2311,7 @@ static my_bool have_committing_connections() if (is_committing_connection(tmp)) { + mysql_mutex_unlock(&LOCK_thread_count); return TRUE; } } -- cgit v1.2.1 From 246e2ae12b514ed3010ffcf6473abbfd9f648340 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Wed, 4 Dec 2019 20:04:45 +0530 Subject: MDEV-20900: IN predicate to IN subquery conversion causes performance regression Disable the IN predicate to IN subquery conversion when the types on the left and right hand side of the IN predicate are not of comparable type. --- sql/item_cmpfunc.h | 1 + sql/opt_subselect.cc | 7 ++++++- sql/sql_tvc.cc | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 24a9991640a..29c32be1c80 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2418,6 +2418,7 @@ public: bool to_be_transformed_into_in_subq(THD *thd); bool create_value_list_for_tvc(THD *thd, List< List > *values); Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg); + uint32 max_length_of_left_expr(); }; class cmp_item_row :public cmp_item diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a4ee27950be..50f7efd6d65 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -829,7 +829,12 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) in_subs->types_allow_materialization= FALSE; // Assign default values in_subs->sjm_scan_allowed= FALSE; - + + /* + The checks here must be kept in sync with the one in + Item_func_in::in_predicate_to_in_subs_transformer(). + */ + bool all_are_fields= TRUE; uint32 total_key_length = 0; for (uint i= 0; i < elements; i++) diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 816c6fe1089..b4538248e07 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -796,6 +796,38 @@ bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) } +/* + @brief + Check whether the items are of comparable type or not + + @details + This check are done because materialization is not performed + if the left expr and right expr are of the same types. + @see subquery_types_allow_materialization() + + @retval + 0 comparable + 1 not comparable +*/ + +static bool cmp_row_types(Item* item1, Item* item2) +{ + uint n= item1->cols(); + if (item2->check_cols(n)) + return true; + + for (uint i=0; i < n; i++) + { + Item *inner= item1->element_index(i); + Item *outer= item2->element_index(i); + if (!inner->type_handler()->subquery_type_allows_materialization(inner, + outer)) + return true; + } + return false; +} + + /** @brief Transform IN predicate into IN subquery @@ -840,10 +872,22 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd, /* SELECT_LEX object where the transformation is performed */ SELECT_LEX *parent_select= lex->current_select; uint8 save_derived_tables= lex->derived_tables; + + /* + Make sure that create_tmp_table will not fail due to too long keys. + Here the strategy would mainly use materialization, so we need to make + sure that the materialized table can be created. + + The checks here are the same as in subquery_type_allows_materialization() + */ + uint32 length= max_length_of_left_expr(); + if (!length || length > tmp_table_max_key_length() || + args[0]->cols() > tmp_table_max_key_parts()) + return this; for (uint i=1; i < arg_count; i++) { - if (!args[i]->const_item()) + if (!args[i]->const_item() || cmp_row_types(args[0], args[i])) return this; } @@ -948,6 +992,16 @@ err: } +uint32 Item_func_in::max_length_of_left_expr() +{ + uint n= args[0]->cols(); + uint32 length= 0; + for (uint i=0; i < n; i++) + length+= args[0]->element_index(i)->max_length; + return length; +} + + /** @brief Check if this IN-predicate can be transformed in IN-subquery -- cgit v1.2.1 From af650c76a63838047b268d8106cd229438f6db92 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Sat, 7 Dec 2019 22:15:38 +0100 Subject: MDEV-18460: Server crashed in strmake / tdc_create_key / THD::create_tmp_table_def_key When there is a WITH clause we postpone check for tables without database for later stages when tables in WITH will be defined. But we should not try to open such tables as temporary tables because temporary tables always belong to a some database. --- sql/temporary_tables.cc | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sql') diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index b97e0334f0d..e2179a71625 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -338,6 +338,13 @@ bool THD::open_temporary_table(TABLE_LIST *tl) DBUG_RETURN(false); } + if (!tl->db) + { + DBUG_PRINT("info", + ("Table reference to a temporary table must have database set")); + DBUG_RETURN(false); + } + /* Temporary tables are not safe for parallel replication. They were designed to be visible to one thread only, so have no table locking. -- cgit v1.2.1 From 280f1c2605c7140af28145dc63e4a3a6177573bf Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 11 Dec 2019 11:13:32 +0100 Subject: MDEV-11345 Compile english error messages into mysqld executable. --- sql/derror.cc | 75 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 22 deletions(-) (limited to 'sql') diff --git a/sql/derror.cc b/sql/derror.cc index f5e63772d54..318800ea262 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -69,6 +69,9 @@ bool init_errmessage(void) { const char **errmsgs; bool error= FALSE; + const char *lang= my_default_lc_messages->errmsgs->language; + my_bool use_english; + DBUG_ENTER("init_errmessage"); free_error_messages(); @@ -77,35 +80,63 @@ bool init_errmessage(void) error_message_charset_info= system_charset_info; - /* Read messages from file. */ - if (read_texts(ERRMSG_FILE, my_default_lc_messages->errmsgs->language, - &original_error_messages)) + use_english= !strcmp(lang, "english"); + if (!use_english) { - /* - No error messages. Create a temporary empty error message so - that we don't get a crash if some code wrongly tries to access - a non existing error message. - */ + /* Read messages from file. */ + use_english= !read_texts(ERRMSG_FILE,lang, &original_error_messages); + error= TRUE; + } + + if (use_english) + { + static const struct + { + const char* name; + uint id; + const char* fmt; + } + english_msgs[]= + { + #include + }; + + memset(errors_per_range, 0, sizeof(errors_per_range)); + /* Calculate nr of messages per range. */ + for (size_t i= 0; i < array_elements(english_msgs); i++) + { + uint id= english_msgs[i].id; + + // We rely on the fact the array is sorted by id. + DBUG_ASSERT(i == 0 || english_msgs[i-1].id < id); + + errors_per_range[id/ERRORS_PER_RANGE-1]= id%ERRORS_PER_RANGE + 1; + } + + size_t all_errors= 0; + for (size_t i= 0; i < MAX_ERROR_RANGES; i++) + all_errors+= errors_per_range[i]; + if (!(original_error_messages= (const char***) - my_malloc(MAX_ERROR_RANGES * sizeof(char**) + - (ERRORS_PER_RANGE * sizeof(char*)), - MYF(0)))) + my_malloc((all_errors + MAX_ERROR_RANGES)* sizeof(void*), + MYF(MY_ZEROFILL)))) DBUG_RETURN(TRUE); - errmsgs= (const char**) (original_error_messages + MAX_ERROR_RANGES); - for (uint i=0 ; i < MAX_ERROR_RANGES ; i++) + errmsgs= (const char**)(original_error_messages + MAX_ERROR_RANGES); + + original_error_messages[0]= errmsgs; + for (uint i= 1; i < MAX_ERROR_RANGES; i++) { - original_error_messages[i]= errmsgs; - errors_per_range[i]= ERRORS_PER_RANGE; + original_error_messages[i]= + original_error_messages[i-1] + errors_per_range[i-1]; } - errors_per_range[2]= 0; // MYSYS error messages - - for (const char **ptr= errmsgs; - ptr < errmsgs + ERRORS_PER_RANGE ; - ptr++) - *ptr= ""; - error= TRUE; + for (uint i= 0; i < array_elements(english_msgs); i++) + { + uint id= english_msgs[i].id; + original_error_messages[id/ERRORS_PER_RANGE-1][id%ERRORS_PER_RANGE]= + english_msgs[i].fmt; + } } /* Register messages for use with my_error(). */ -- cgit v1.2.1 From 808036a61d13d4392b6e0d9e7e9eca87a0c20495 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 12 Dec 2019 03:45:34 +0530 Subject: MDEV-19380: ASAN heap-use-after-free in Protocol::net_store_data The issue here is window function makes the passed string object to point to an area in a temporary table's record buffer. Then, the temporary table is freed, together with its record buffer. Then, Item_cache_str attempts to read this value. The fix is to call value_buff.copy(). This will make the value_buff to store its string in a buffer that it owns, which will not disappear unexpectedly. --- sql/item.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 333d71ddf70..10087ef1974 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10044,6 +10044,8 @@ bool Item_cache_str::cache_value() value_buff.copy(*value); value= &value_buff; } + else + value_buff.copy(); return TRUE; } -- cgit v1.2.1 From e0f9540bcc6ab1618b6fd475f02e019401c4c295 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 11 Dec 2019 15:09:17 +0400 Subject: MDEV-20667 Server crash on pop_cursor When backpatching a forward GOTO label, the old code erroneously used CURSOR/HANDLER difference between context frames "c" and "a" to tune a cpop/hpop command. So the cpop/hpop command later tried to pop all cursors/handlers declared between "a" and "c", but those between "b" and "c" were not cpushed/hpoped yet during the execution of "GOTO x". Fixing the code to use the difference between frames "b" and "a" only. BEGIN -- a ... GOTO x; -- b ... <> -- c ... END -- d --- sql/sp_head.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ab7faf51849..360713fc452 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2571,7 +2571,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) } if (bp->instr_type == CPOP) { - uint n= lab->ctx->diff_cursors(lab_begin_block->ctx, true); + uint n= bp->instr->m_ctx->diff_cursors(lab_begin_block->ctx, true); if (n == 0) { // Remove cpop instr @@ -2588,7 +2588,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) } if (bp->instr_type == HPOP) { - uint n= lab->ctx->diff_handlers(lab_begin_block->ctx, true); + uint n= bp->instr->m_ctx->diff_handlers(lab_begin_block->ctx, true); if (n == 0) { // Remove hpop instr @@ -3082,6 +3082,8 @@ void sp_head::optimize() sp_instr *i; uint src, dst; + DBUG_EXECUTE_IF("sp_head_optimize_disable", return; ); + opt_mark(); bp.empty(); -- cgit v1.2.1