From 3c6c0ebc6a31835260d07463d581b9715776812f Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Mon, 9 Jan 2017 20:09:57 +0530 Subject: BUG#25250768: WRITING ON A READ_ONLY=ON SERVER WITHOUT SUPER PRIVILEGE. Backport from mysql-5.7 to mysql-5.5 and mysql-5.6. BUG#13969578: TEMPORARY TABLE IN A DATABASE ON A READ-ONLY INSTANCE CAN BE OVERWRITTEN Analysis: ======== Creation or modification of a persistent table by a non-super user is NOT ALLOWED in read_only mode. Only TEMPORARY tables are allowed to be created or modified in read_only mode. But the creation of a persistent table was being allowed when a temporary table of the same name existed. The routine which denies updating a non-temporary table in a read_only mode does not handle the case of creation of a regular table when a temporary table of the same exists. Fix: === Handled the condition where an attempt is made to create a persistent table having the same name as that of the temporary table. Hence the creation of a persistent table by a non-super user when a temporary table of the same exists is denied under read_only mode. --- sql/sql_parse.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sql/sql_parse.cc') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 86763b6e3de..82ab76b2e1c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -822,14 +822,18 @@ static my_bool deny_updates_if_read_only_option(THD *thd, (lex->sql_command == SQLCOM_CREATE_TABLE) && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); + const my_bool create_real_tables= + (lex->sql_command == SQLCOM_CREATE_TABLE) && + !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); + const my_bool drop_temp_tables= (lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary; const my_bool update_real_tables= - some_non_temp_table_to_be_updated(thd, all_tables) && - !(create_temp_tables || drop_temp_tables); - + ((create_real_tables || + some_non_temp_table_to_be_updated(thd, all_tables)) && + !(create_temp_tables || drop_temp_tables)); const my_bool create_or_drop_databases= (lex->sql_command == SQLCOM_CREATE_DB) || -- cgit v1.2.1 From 9b3360ea4417ed653d5c7eed29f4ef7e80618e43 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 18 Jul 2017 14:47:40 +0200 Subject: BUG#25250768: WRITING ON A READ_ONLY=ON SERVER WITHOUT SUPER PRIVILEGE simplify. add a test case. --- sql/sql_parse.cc | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'sql/sql_parse.cc') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 83cd6cccba5..1d596ed9df7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -835,24 +835,16 @@ static my_bool deny_updates_if_read_only_option(THD *thd, if (lex->sql_command == SQLCOM_UPDATE_MULTI) DBUG_RETURN(FALSE); - const my_bool create_temp_tables= - (lex->sql_command == SQLCOM_CREATE_TABLE) && - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - - const my_bool create_real_tables= - (lex->sql_command == SQLCOM_CREATE_TABLE) && - !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - - const my_bool drop_temp_tables= - (lex->sql_command == SQLCOM_DROP_TABLE) && - lex->drop_temporary; - - const my_bool update_real_tables= - ((create_real_tables || - some_non_temp_table_to_be_updated(thd, all_tables)) && - !(create_temp_tables || drop_temp_tables)); + /* + a table-to-be-created is not in the temp table list yet, + so CREATE TABLE needs a special treatment + */ + const bool update_real_tables= + lex->sql_command == SQLCOM_CREATE_TABLE + ? !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + : some_non_temp_table_to_be_updated(thd, all_tables); - const my_bool create_or_drop_databases= + const bool create_or_drop_databases= (lex->sql_command == SQLCOM_CREATE_DB) || (lex->sql_command == SQLCOM_DROP_DB); -- cgit v1.2.1 From 58aaae6f2a23e68b23727016844d177f178ea8ff Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 18 Jul 2017 16:42:40 +0200 Subject: ensure that filename in COM_BINLOG_DUMP isn't too long --- sql/sql_parse.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sql/sql_parse.cc') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1d596ed9df7..ba0520de4bb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1271,9 +1271,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, kill_zombie_dump_threads(slave_server_id); thd->server_id = slave_server_id; - general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10, - (long) pos); - mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); + const char *name= packet + 10; + size_t nlen= strlen(name); + + general_log_print(thd, command, "Log: '%s' Pos: %lu", name, pos); + if (nlen < FN_REFLEN) + mysql_binlog_send(thd, thd->strmake(name, nlen), (my_off_t)pos, flags); unregister_slave(thd,1,1); /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; -- cgit v1.2.1 From 74543698a76c02d1a81e17e5918ecbf6b795607b Mon Sep 17 00:00:00 2001 From: Monty Date: Sat, 5 Aug 2017 19:26:10 +0300 Subject: MDEV-13179 main.errors fails with wrong errno The problem was that the introduction of max-thread-mem-used can cause an allocation error very early, even before mysql_parse() is called. As mysql_parse() calls thd->reset_for_next_command(), which called clear_error(), the error number was lost. Fixed by adding an option to have unique messages for each KILL signal and change max-thread-mem-used to use this new feature. This removes a lot of problems with the original approach, where one could get errors signaled silenty almost any time. ixed by moving clear_error() from reset_for_next_command() to do_command(), before any memory allocation for the thread. Related changes: - reset_for_next_command() now have an optional parameter if we should call clear_error() or not. By default it's called, but not anymore from dispatch_command() which was the original problem. - Added optional paramater to clear_error() to force calling of reset_diagnostics_area(). Before clear_error() only called reset_diagnostics_area() if there was no error, so we normally called reset_diagnostics_area() twice. - This change removed several duplicated calls to clear_error() when starting a query. - Reset max_mem_used on COM_QUIT, to protect against kill during quit. - Use fatal_error() instead of setting is_fatal_error (cleanup) - Set fatal_error if max_thead_mem_used is signaled. (Same logic we use for other places where we are out of resources) --- sql/sql_parse.cc | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'sql/sql_parse.cc') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c4b32933b8a..a1da14d5c53 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -669,6 +669,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, */ save_vio= thd->net.vio; thd->net.vio= 0; + thd->clear_error(1); dispatch_command(COM_QUERY, thd, buf, len); thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; @@ -800,6 +801,7 @@ static void handle_bootstrap_impl(THD *thd) if (bootstrap_error) break; + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); thd->lex->restore_set_statement_var(); @@ -954,13 +956,8 @@ bool do_command(THD *thd) if(!thd->skip_wait_timeout) my_net_set_read_timeout(net, thd->variables.net_wait_timeout); - - /* - XXX: this code is here only to clear possible errors of init_connect. - Consider moving to init_connect() instead. - */ - thd->clear_error(); // Clear error message - thd->get_stmt_da()->reset_diagnostics_area(); + /* Errors and diagnostics are cleared once here before query */ + thd->clear_error(1); net_new_transaction(net); @@ -1123,6 +1120,7 @@ bool do_command(THD *thd) WSREP_WARN("For retry temporally setting character set to : %s", my_charset_latin1.csname); } + thd->clear_error(); return_value= dispatch_command(command, thd, thd->wsrep_retry_query, thd->wsrep_retry_query_len); thd->variables.character_set_client = current_charset; @@ -1272,7 +1270,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); WSREP_DEBUG("Deadlock error for: %s", thd->query()); mysql_mutex_unlock(&thd->LOCK_wsrep_thd); - thd->killed = NOT_KILLED; + thd->reset_killed(); thd->mysys_var->abort = 0; thd->wsrep_conflict_state = NO_CONFLICT; thd->wsrep_retry_counter = 0; @@ -1625,7 +1623,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } packet= arg_end + 1; - thd->reset_for_next_command(); + thd->reset_for_next_command(0); // Don't clear errors lex_start(thd); /* Must be before we init the table list. */ if (lower_case_table_names) @@ -1694,7 +1692,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #endif case COM_QUIT: - /* We don't calculate statistics for this command */ + /* Note: We don't calculate statistics for this command */ + + /* Ensure that quit works even if max_mem_used is set */ + thd->variables.max_mem_used= LONGLONG_MAX; general_log_print(thd, command, NullS); net->error=0; // Don't give 'abort' message thd->get_stmt_da()->disable_status(); // Don't send anything back @@ -1974,6 +1975,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, dec_thread_running(); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); #if defined(ENABLED_PROFILING) @@ -5047,7 +5049,7 @@ end_with_restore_list: /* Disconnect the current client connection. */ if (tx_release) { - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); thd->print_aborted_warning(3, "RELEASE"); } #ifdef WITH_WSREP @@ -5093,7 +5095,7 @@ end_with_restore_list: } /* Disconnect the current client connection. */ if (tx_release) - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); #ifdef WITH_WSREP if (WSREP(thd) && thd->wsrep_conflict_state != NO_CONFLICT) { @@ -6879,6 +6881,8 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) Reset the part of THD responsible for the state of command processing. + @param do_clear_error Set if we should clear errors + This needs to be called before execution of every statement (prepared or conventional). It is not called by substatements of routines. @@ -6886,12 +6890,16 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) @todo Call it after we use THD for queries, not before. */ -void THD::reset_for_next_command() +void THD::reset_for_next_command(bool do_clear_error) { THD *thd= this; DBUG_ENTER("THD::reset_for_next_command"); DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ DBUG_ASSERT(! thd->in_sub_stmt); + + if (do_clear_error) + clear_error(1); + thd->free_list= 0; thd->select_number= 1; /* @@ -6947,8 +6955,6 @@ void THD::reset_for_next_command() reset_dynamic(&thd->user_var_events); thd->user_var_events_alloc= thd->mem_root; } - thd->clear_error(); - thd->get_stmt_da()->reset_diagnostics_area(); thd->get_stmt_da()->reset_for_next_command(); thd->rand_used= 0; thd->m_sent_row_count= thd->m_examined_row_count= 0; @@ -7180,7 +7186,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->wsrep_conflict_state == CERT_FAILURE) { thd->reset_for_next_command(); - thd->killed= NOT_KILLED; + thd->reset_killed(); if (is_autocommit && thd->lex->sql_command != SQLCOM_SELECT && (thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit)) @@ -7208,7 +7214,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->thread_id, is_autocommit, thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); - thd->killed= NOT_KILLED; + thd->reset_killed(); thd->wsrep_conflict_state= NO_CONFLICT; if (thd->wsrep_conflict_state != REPLAYING) thd->wsrep_retry_counter= 0; // reset @@ -8282,10 +8288,10 @@ void sql_kill(THD *thd, longlong id, killed_state state, killed_type type) uint error; if (!(error= kill_one_thread(thd, id, state, type))) { - if ((!thd->killed)) + if (!thd->killed) my_ok(thd); else - my_error(killed_errno(thd->killed), MYF(0), id); + thd->send_kill_message(); } else my_error(error, MYF(0), id); -- cgit v1.2.1