diff options
author | Tatiana A. Nurnberg <azundris@mysql.com> | 2009-07-31 21:58:40 +0200 |
---|---|---|
committer | Tatiana A. Nurnberg <azundris@mysql.com> | 2009-07-31 21:58:40 +0200 |
commit | e26350e000e1967592030270b6cd90d470043ce1 (patch) | |
tree | 9ef8cb8d8fe75f8505b899addc69d86dbdfa56eb /sql | |
parent | 717d6054f516baa35e14ac11f0a06c630b9e9fd9 (diff) | |
parent | 4e95179af9c2f2589093ea9f94472d8d047892c7 (diff) | |
download | mariadb-git-e26350e000e1967592030270b6cd90d470043ce1.tar.gz |
auto-merge
Diffstat (limited to 'sql')
47 files changed, 954 insertions, 395 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 0eba357b632..dba32cac6b2 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1430,13 +1430,7 @@ Event_job_data::execute(THD *thd, bool drop) thd->variables.sql_mode= sql_mode; thd->variables.time_zone= time_zone; - /* - Peculiar initialization order is a crutch to avoid races in SHOW - PROCESSLIST which reads thd->{query/query_length} without a mutex. - */ - thd->query_length= 0; - thd->query= sp_sql.c_ptr_safe(); - thd->query_length= sp_sql.length(); + thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length()); { Parser_state parser_state(thd, thd->query, thd->query_length); @@ -1497,13 +1491,8 @@ end_no_lex_start: else { ulong saved_master_access; - /* - Peculiar initialization order is a crutch to avoid races in SHOW - PROCESSLIST which reads thd->{query/query_length} without a mutex. - */ - thd->query_length= 0; - thd->query= sp_sql.c_ptr_safe(); - thd->query_length= sp_sql.length(); + + thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length()); /* NOTE: even if we run in read-only mode, we should be able to lock @@ -1528,8 +1517,7 @@ end_no_lex_start: thd->end_statement(); thd->cleanup_after_query(); /* Avoid races with SHOW PROCESSLIST */ - thd->query_length= 0; - thd->query= NULL; + thd->set_query(NULL, 0); DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret)); diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index d9d010783e8..8c0025f9ed4 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -158,6 +158,7 @@ deinit_event_thread(THD *thd) thread_count--; thread_running--; delete thd; + pthread_cond_broadcast(&COND_thread_count); pthread_mutex_unlock(&LOCK_thread_count); } @@ -418,6 +419,7 @@ Event_scheduler::start() thread_count--; thread_running--; delete new_thd; + pthread_cond_broadcast(&COND_thread_count); pthread_mutex_unlock(&LOCK_thread_count); } end: @@ -550,6 +552,7 @@ error: thread_count--; thread_running--; delete new_thd; + pthread_cond_broadcast(&COND_thread_count); pthread_mutex_unlock(&LOCK_thread_count); } delete event_name; @@ -625,13 +628,13 @@ Event_scheduler::stop() DBUG_PRINT("info", ("Scheduler thread has id %lu", scheduler_thd->thread_id)); /* Lock from delete */ - pthread_mutex_lock(&scheduler_thd->LOCK_delete); + pthread_mutex_lock(&scheduler_thd->LOCK_thd_data); /* This will wake up the thread if it waits on Queue's conditional */ sql_print_information("Event Scheduler: Killing the scheduler thread, " "thread id %lu", scheduler_thd->thread_id); scheduler_thd->awake(THD::KILL_CONNECTION); - pthread_mutex_unlock(&scheduler_thd->LOCK_delete); + pthread_mutex_unlock(&scheduler_thd->LOCK_thd_data); /* thd could be 0x0, when shutting down */ sql_print_information("Event Scheduler: " diff --git a/sql/events.cc b/sql/events.cc index ea935e67bd3..10edfff2402 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -852,22 +852,23 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) } -/* - Inits the scheduler's structures. +/** + Initializes the scheduler's structures. - SYNOPSIS - Events::init() + @param opt_noacl_or_bootstrap + TRUE if there is --skip-grant-tables or --bootstrap + option. In that case we disable the event scheduler. - NOTES - This function is not synchronized. + @note This function is not synchronized. - RETURN VALUE - FALSE OK - TRUE Error in case the scheduler can't start + @retval FALSE Perhaps there was an error, and the event scheduler + is disabled. But the error is not fatal and the + server start up can continue. + @retval TRUE Fatal error. Startup must terminate (call unireg_abort()). */ bool -Events::init(my_bool opt_noacl) +Events::init(my_bool opt_noacl_or_bootstrap) { THD *thd; @@ -875,11 +876,6 @@ Events::init(my_bool opt_noacl) DBUG_ENTER("Events::init"); - /* Disable the scheduler if running with --skip-grant-tables */ - if (opt_noacl) - opt_event_scheduler= EVENTS_DISABLED; - - /* We need a temporary THD during boot */ if (!(thd= new THD())) { @@ -908,23 +904,30 @@ Events::init(my_bool opt_noacl) /* Since we allow event DDL even if the scheduler is disabled, check the system tables, as we might need them. + + If run with --skip-grant-tables or --bootstrap, don't try to do the + check of system tables and don't complain: in these modes the tables + are most likely not there and we're going to disable the event + scheduler anyway. */ - if (Event_db_repository::check_system_tables(thd)) + if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd)) { - sql_print_error("Event Scheduler: An error occurred when initializing " - "system tables.%s", - opt_event_scheduler == EVENTS_DISABLED ? - "" : " Disabling the Event Scheduler."); + if (! opt_noacl_or_bootstrap) + { + sql_print_error("Event Scheduler: An error occurred when initializing " + "system tables. Disabling the Event Scheduler."); + check_system_tables_error= TRUE; + } /* Disable the scheduler since the system tables are not up to date */ opt_event_scheduler= EVENTS_DISABLED; - check_system_tables_error= TRUE; goto end; } /* Was disabled explicitly from the command line, or because we're running - with --skip-grant-tables, or because we have no system tables. + with --skip-grant-tables, or --bootstrap, or because we have no system + tables. */ if (opt_event_scheduler == Events::EVENTS_DISABLED) goto end; @@ -941,7 +944,7 @@ Events::init(my_bool opt_noacl) } if (event_queue->init_queue(thd) || load_events_from_db(thd) || - opt_event_scheduler == EVENTS_ON && scheduler->start()) + (opt_event_scheduler == EVENTS_ON && scheduler->start())) { sql_print_error("Event Scheduler: Error while loading from disk."); res= TRUE; /* fatal error: request unireg_abort */ diff --git a/sql/field.cc b/sql/field.cc index ed085de1db3..452dfc3ae55 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6271,48 +6271,15 @@ check_string_copy_error(Field_str *field, const char *end, CHARSET_INFO *cs) { - const char *pos, *end_orig; - char tmp[64], *t; + const char *pos; + char tmp[32]; if (!(pos= well_formed_error_pos) && !(pos= cannot_convert_error_pos)) return FALSE; - end_orig= end; - set_if_smaller(end, pos + 6); + convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); - for (t= tmp; pos < end; pos++) - { - /* - If the source string is ASCII compatible (mbminlen==1) - and the source character is in ASCII printable range (0x20..0x7F), - then display the character as is. - - Otherwise, if the source string is not ASCII compatible (e.g. UCS2), - or the source character is not in the printable range, - then print the character using HEX notation. - */ - if (((unsigned char) *pos) >= 0x20 && - ((unsigned char) *pos) <= 0x7F && - cs->mbminlen == 1) - { - *t++= *pos; - } - else - { - *t++= '\\'; - *t++= 'x'; - *t++= _dig_vec_upper[((unsigned char) *pos) >> 4]; - *t++= _dig_vec_upper[((unsigned char) *pos) & 15]; - } - } - if (end_orig > end) - { - *t++= '.'; - *t++= '.'; - *t++= '.'; - } - *t= '\0'; push_warning_printf(field->table->in_use, field->table->in_use->abort_on_warning ? MYSQL_ERROR::WARN_LEVEL_ERROR : diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index f705af8bf47..a3adbe4a952 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -253,8 +253,7 @@ static void run_query(THD *thd, char *buf, char *end, const char* found_semicolon= NULL; bzero((char*) &thd->net, sizeof(NET)); - thd->query_length= end - buf; - thd->query= buf; + thd->set_query(buf, (uint) (end - buf)); thd->variables.pseudo_thread_id= thread_id; thd->transaction.stmt.modified_non_trans_table= FALSE; if (disable_binlog) @@ -297,8 +296,7 @@ static void run_query(THD *thd, char *buf, char *end, thd->main_da.reset_diagnostics_area(); thd->options= save_thd_options; - thd->query_length= save_thd_query_length; - thd->query= save_thd_query; + thd->set_query(save_thd_query, save_thd_query_length); thd->variables.pseudo_thread_id= save_thread_id; thd->status_var= save_thd_status_var; thd->transaction.all= save_thd_transaction_all; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 97ecd70d6f8..562716a7db7 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -423,12 +423,9 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root) int ha_partition::delete_table(const char *name) { - int error; DBUG_ENTER("ha_partition::delete_table"); - if ((error= del_ren_cre_table(name, NULL, NULL, NULL))) - DBUG_RETURN(error); - DBUG_RETURN(handler::delete_table(name)); + DBUG_RETURN(del_ren_cre_table(name, NULL, NULL, NULL)); } @@ -456,12 +453,9 @@ int ha_partition::delete_table(const char *name) int ha_partition::rename_table(const char *from, const char *to) { - int error; DBUG_ENTER("ha_partition::rename_table"); - if ((error= del_ren_cre_table(from, to, NULL, NULL))) - DBUG_RETURN(error); - DBUG_RETURN(handler::rename_table(from, to)); + DBUG_RETURN(del_ren_cre_table(from, to, NULL, NULL)); } @@ -1807,6 +1801,15 @@ uint ha_partition::del_ren_cre_table(const char *from, DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to)); name_buffer_ptr= m_name_buffer_ptr; file= m_file; + if (to == NULL && table_arg == NULL) + { + /* + Delete table, start by delete the .par file. If error, break, otherwise + delete as much as possible. + */ + if ((error= handler::delete_table(from))) + DBUG_RETURN(error); + } /* Since ha_partition has HA_FILE_BASED, it must alter underlying table names if they do not have HA_FILE_BASED and lower_case_table_names == 2. @@ -1828,6 +1831,8 @@ uint ha_partition::del_ren_cre_table(const char *from, create_partition_name(to_buff, to_path, name_buffer_ptr, NORMAL_PART_NAME, FALSE); error= (*file)->ha_rename_table(from_buff, to_buff); + if (error) + goto rename_error; } else if (table_arg == NULL) // delete branch error= (*file)->ha_delete_table(from_buff); @@ -1843,6 +1848,15 @@ uint ha_partition::del_ren_cre_table(const char *from, save_error= error; i++; } while (*(++file)); + if (to != NULL) + { + if ((error= handler::rename_table(from, to))) + { + /* Try to revert everything, ignore errors */ + (void) handler::rename_table(to, from); + goto rename_error; + } + } DBUG_RETURN(save_error); create_error: name_buffer_ptr= m_name_buffer_ptr; @@ -1850,7 +1864,21 @@ create_error: { create_partition_name(from_buff, from_path, name_buffer_ptr, NORMAL_PART_NAME, FALSE); - VOID((*file)->ha_delete_table((const char*) from_buff)); + (void) (*file)->ha_delete_table((const char*) from_buff); + name_buffer_ptr= strend(name_buffer_ptr) + 1; + } + DBUG_RETURN(error); +rename_error: + name_buffer_ptr= m_name_buffer_ptr; + for (abort_file= file, file= m_file; file < abort_file; file++) + { + /* Revert the rename, back from 'to' to the original 'from' */ + create_partition_name(from_buff, from_path, name_buffer_ptr, + NORMAL_PART_NAME, FALSE); + create_partition_name(to_buff, to_path, name_buffer_ptr, + NORMAL_PART_NAME, FALSE); + /* Ignore error here */ + (void) (*file)->ha_rename_table(to_buff, from_buff); name_buffer_ptr= strend(name_buffer_ptr) + 1; } DBUG_RETURN(error); @@ -3179,6 +3207,7 @@ int ha_partition::delete_row(const uchar *buf) int ha_partition::delete_all_rows() { int error; + bool truncate= FALSE; handler **file; THD *thd= ha_thd(); DBUG_ENTER("ha_partition::delete_all_rows"); @@ -3190,12 +3219,16 @@ int ha_partition::delete_all_rows() ha_data->next_auto_inc_val= 0; ha_data->auto_inc_initialized= FALSE; unlock_auto_increment(); + truncate= TRUE; } file= m_file; do { if ((error= (*file)->ha_delete_all_rows())) DBUG_RETURN(error); + /* Ignore the error */ + if (truncate) + (void) (*file)->ha_reset_auto_increment(0); } while (*(++file)); DBUG_RETURN(0); } diff --git a/sql/handler.cc b/sql/handler.cc index e65ceba4181..e5c64452aaf 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -62,7 +62,9 @@ static const LEX_STRING sys_table_aliases[]= }; const char *ha_row_type[] = { - "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE", "?","?","?" + "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", + /* Reserved to be "PAGE" in future versions */ "?", + "?","?","?" }; const char *tx_isolation_names[] = @@ -342,6 +344,7 @@ int ha_init_errors(void) SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY)); SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED)); SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE)); + SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER(ER_TOO_MANY_CONCURRENT_TRXS)); /* Register the error messages for use with my_error(). */ return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -2747,6 +2750,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_AUTOINC_ERANGE: textno= ER_WARN_DATA_OUT_OF_RANGE; break; + case HA_ERR_TOO_MANY_CONCURRENT_TRXS: + textno= ER_TOO_MANY_CONCURRENT_TRXS; + break; default: { /* The error was "unknown" to this function. @@ -2973,6 +2979,7 @@ uint handler::get_dup_key(int error) */ int handler::delete_table(const char *name) { + int saved_error= 0; int error= 0; int enoent_or_zero= ENOENT; // Error if no file was deleted char buff[FN_REFLEN]; @@ -2982,21 +2989,31 @@ int handler::delete_table(const char *name) fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT); if (my_delete_with_symlink(buff, MYF(0))) { - if ((error= my_errno) != ENOENT) - break; + if (my_errno != ENOENT) + { + /* + If error on the first existing file, return the error. + Otherwise delete as much as possible. + */ + if (enoent_or_zero) + return my_errno; + saved_error= my_errno; + } } else enoent_or_zero= 0; // No error for ENOENT error= enoent_or_zero; } - return error; + return saved_error ? saved_error : error; } int handler::rename_table(const char * from, const char * to) { int error= 0; - for (const char **ext= bas_ext(); *ext ; ext++) + const char **ext, **start_ext; + start_ext= bas_ext(); + for (ext= start_ext; *ext ; ext++) { if (rename_file_ext(from, to, *ext)) { @@ -3005,6 +3022,12 @@ int handler::rename_table(const char * from, const char * to) error= 0; } } + if (error) + { + /* Try to revert the rename. Ignore errors. */ + for (; ext >= start_ext; ext--) + rename_file_ext(to, from, *ext); + } return error; } diff --git a/sql/handler.h b/sql/handler.h index 5c7cfa4d58b..01f673ecd51 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -280,7 +280,9 @@ enum legacy_db_type enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED, - ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGE }; + ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, + /** Unused. Reserved for future versions. */ + ROW_TYPE_PAGE }; enum enum_binlog_func { BFN_RESET_LOGS= 1, @@ -323,7 +325,9 @@ enum enum_binlog_command { #define HA_CREATE_USED_PASSWORD (1L << 17) #define HA_CREATE_USED_CONNECTION (1L << 18) #define HA_CREATE_USED_KEY_BLOCK_SIZE (1L << 19) +/** Unused. Reserved for future versions. */ #define HA_CREATE_USED_TRANSACTIONAL (1L << 20) +/** Unused. Reserved for future versions. */ #define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21) typedef ulonglong my_xid; // this line is the same as in log_event.h @@ -914,13 +918,14 @@ typedef struct st_ha_create_information uint options; /* OR of HA_CREATE_ options */ uint merge_insert_method; uint extra_size; /* length of extra data segment */ - /* 0 not used, 1 if not transactional, 2 if transactional */ + /** Transactional or not. Unused; reserved for future versions. */ enum ha_choice transactional; bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ - enum ha_choice page_checksum; /* If we have page_checksums */ + /** Per-page checksums or not. Unused; reserved for future versions. */ + enum ha_choice page_checksum; } HA_CREATE_INFO; diff --git a/sql/item.cc b/sql/item.cc index 4d9004fff26..2640b74851b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -599,6 +599,7 @@ bool Item_ident::remove_dependence_processor(uchar * arg) DBUG_ENTER("Item_ident::remove_dependence_processor"); if (depended_from == (st_select_lex *) arg) depended_from= 0; + context= &((st_select_lex *) arg)->context; DBUG_RETURN(0); } diff --git a/sql/item_func.cc b/sql/item_func.cc index 0af3c4954cd..55d4b37ddb0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5990,6 +5990,9 @@ Item_func_sp::execute_impl(THD *thd) #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx= thd->security_ctx; #endif + enum enum_sp_data_access access= + (m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ? + SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess; DBUG_ENTER("Item_func_sp::execute_impl"); @@ -6007,11 +6010,13 @@ Item_func_sp::execute_impl(THD *thd) Throw an error if a non-deterministic function is called while statement-based replication (SBR) is active. */ + if (!m_sp->m_chistics->detistic && !trust_function_creators && + (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) && (mysql_bin_log.is_open() && thd->variables.binlog_format == BINLOG_FORMAT_STMT)) { - my_error(ER_BINLOG_ROW_RBR_TO_SBR, MYF(0)); + my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0)); goto error; } diff --git a/sql/item_func.h b/sql/item_func.h index 514f93a39ea..025ac12fe07 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1519,6 +1519,7 @@ public: ft_handler->please->close_search(ft_handler); ft_handler= 0; concat_ws= 0; + table= 0; // required by Item_func_match::eq() DBUG_VOID_RETURN; } enum Functype functype() const { return FT_FUNC; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 00c09679737..cdb091fa07e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1227,6 +1227,10 @@ Item_in_subselect::single_value_transformer(JOIN *join, else { // it is single select without tables => possible optimization + // remove the dependence mark since the item is moved to upper + // select and is not outer anymore. + item->walk(&Item::remove_dependence_processor, 0, + (uchar *) select_lex->outer_select()); item= func->create(left_expr, item); // fix_field of item will be done in time of substituting substitution= item; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 46a58351872..38251294053 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3327,8 +3327,13 @@ bool Item_func_group_concat::add() TREE_ELEMENT *el= 0; // Only for safety if (row_eligible && tree) + { el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0, tree->custom_arg); + /* check if there was enough memory to insert the row */ + if (!el) + return 1; + } /* If the row is not a duplicate (el->count == 1) we can dump the row here in case of GROUP_CONCAT(DISTINCT...) diff --git a/sql/lex.h b/sql/lex.h index acb81dcf717..0a85824f6f7 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -387,7 +387,6 @@ static SYMBOL symbols[] = { { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, { "PARSER", SYM(PARSER_SYM)}, { "PAGE", SYM(PAGE_SYM)}, - { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, { "PARTIAL", SYM(PARTIAL)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, @@ -543,7 +542,6 @@ static SYMBOL symbols[] = { { "TO", SYM(TO_SYM)}, { "TRAILING", SYM(TRAILING)}, { "TRANSACTION", SYM(TRANSACTION_SYM)}, - { "TRANSACTIONAL", SYM(TRANSACTIONAL_SYM)}, { "TRIGGER", SYM(TRIGGER_SYM)}, { "TRIGGERS", SYM(TRIGGERS_SYM)}, { "TRUE", SYM(TRUE_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index 8bb6ba8e9c6..bb81d0c723e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1564,25 +1564,15 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) YESNO(all), YESNO(thd->transaction.all.modified_non_trans_table), YESNO(thd->transaction.stmt.modified_non_trans_table))); - if ((all && thd->transaction.all.modified_non_trans_table) || - (!all && thd->transaction.stmt.modified_non_trans_table && - !mysql_bin_log.check_write_error(thd)) || - ((thd->options & OPTION_KEEP_LOG) && - !mysql_bin_log.check_write_error(thd))) + if (mysql_bin_log.check_write_error(thd)) { /* - We write the transaction cache with a rollback last if we have - modified any non-transactional table. We do this even if we are - committing a single statement that has modified a - non-transactional table since it can have modified a - transactional table in that statement as well, which needs to be - rolled back on the slave. + "all == true" means that a "rollback statement" triggered the error and + this function was called. However, this must not happen as a rollback + is written directly to the binary log. And in auto-commit mode, a single + statement that is rolled back has the flag all == false. */ - Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0); - error= binlog_end_trans(thd, trx_data, &qev, all); - } - else - { + DBUG_ASSERT(!all); /* We reach this point if either only transactional tables were modified or the effect of a statement that did not get into the binlog needs to be @@ -1592,13 +1582,39 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) on the master did not get into the binlog and slaves will be inconsistent. On the other hand, if a statement is transactional, we just safely roll it back. - */ + */ if ((thd->transaction.stmt.modified_non_trans_table || (thd->options & OPTION_KEEP_LOG)) && mysql_bin_log.check_write_error(thd)) trx_data->set_incident(); error= binlog_end_trans(thd, trx_data, 0, all); } + else + { + /* + We flush the cache with a rollback, wrapped in a beging/rollback if: + . aborting a transcation that modified a non-transactional table or; + . aborting a statement that modified both transactional and + non-transctional tables but which is not in the boundaries of any + transaction; + . the OPTION_KEEP_LOG is activate. + */ + if ((all && thd->transaction.all.modified_non_trans_table) || + (!all && thd->transaction.stmt.modified_non_trans_table && + !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) || + ((thd->options & OPTION_KEEP_LOG))) + { + Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0); + error= binlog_end_trans(thd, trx_data, &qev, all); + } + /* + Otherwise, we simply truncate the cache as there is no change on + non-transactional tables as follows. + */ + else if ((all && !thd->transaction.all.modified_non_trans_table) || + (!all && !thd->transaction.stmt.modified_non_trans_table)) + error= binlog_end_trans(thd, trx_data, 0, all); + } if (!all) trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback DBUG_RETURN(error); diff --git a/sql/log_event.cc b/sql/log_event.cc index 5f77ab3dcc4..e7cbbaba38e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3025,8 +3025,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, rpl_filter->db_ok(thd->db)) { thd->set_time((time_t)when); - thd->query_length= q_len_arg; - thd->query= (char*)query_arg; + thd->set_query((char*)query_arg, q_len_arg); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id = next_query_id(); VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -3231,7 +3230,6 @@ Default database: '%s'. Query: '%s'", } /* End of if (db_ok(... */ end: - VOID(pthread_mutex_lock(&LOCK_thread_count)); /* Probably we have set thd->query, thd->db, thd->catalog to point to places in the data_buf of this event. Now the event is going to be deleted @@ -3244,10 +3242,8 @@ end: */ thd->catalog= 0; thd->set_db(NULL, 0); /* will free the current database */ + thd->set_query(NULL, 0); DBUG_PRINT("info", ("end: query= 0")); - thd->query= 0; // just to be sure - thd->query_length= 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); /* As a disk space optimization, future masters will not log an event for @@ -4557,8 +4553,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start, (char **)&thd->lex->fname_end); *end= 0; - thd->query_length= end - load_data_query; - thd->query= load_data_query; + thd->set_query(load_data_query, (uint) (end - load_data_query)); if (sql_ex.opt_flags & REPLACE_FLAG) { @@ -4664,12 +4659,9 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, error: thd->net.vio = 0; const char *remember_db= thd->db; - VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->catalog= 0; thd->set_db(NULL, 0); /* will free the current database */ - thd->query= 0; - thd->query_length= 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->set_query(NULL, 0); close_thread_tables(thd); DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index de02abc48e3..03c366a45bf 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1976,6 +1976,7 @@ extern bool opt_disable_networking, opt_skip_show_db; extern bool opt_ignore_builtin_innodb; extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress; +extern bool in_bootstrap; extern uint volatile thread_count, thread_running, global_read_lock; extern uint connection_count; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f7ee7b025f9..49729b5eaea 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -416,6 +416,21 @@ my_bool locked_in_memory; bool opt_using_transactions; bool volatile abort_loop; bool volatile shutdown_in_progress; +/* + True if the bootstrap thread is running. Protected by LOCK_thread_count, + just like thread_count. + Used in bootstrap() function to determine if the bootstrap thread + has completed. Note, that we can't use 'thread_count' instead, + since in 5.1, in presence of the Event Scheduler, there may be + event threads running in parallel, so it's impossible to know + what value of 'thread_count' is a sign of completion of the + bootstrap thread. + + At the same time, we can't start the event scheduler after + bootstrap either, since we want to be able to process event-related + SQL commands in the init file and in --bootstrap mode. +*/ +bool in_bootstrap= FALSE; /** @brief 'grant_option' is used to indicate if privileges needs to be checked, in which case the lock, LOCK_grant, is used @@ -3504,7 +3519,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_lock_db,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW); - (void) pthread_mutex_init(&LOCK_open, NULL); + (void) pthread_mutex_init(&LOCK_open, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_status,MY_MUTEX_INIT_FAST); @@ -3651,14 +3666,17 @@ static void init_ssl() #ifdef HAVE_OPENSSL if (opt_use_ssl) { + enum enum_ssl_init_error error= SSL_INITERR_NOERROR; + /* having ssl_acceptor_fd != 0 signals the use of SSL */ ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, - opt_ssl_cipher); + opt_ssl_cipher, &error); DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd)); if (!ssl_acceptor_fd) { sql_print_warning("Failed to setup SSL"); + sql_print_warning("SSL error: %s", sslGetErrString(error)); opt_use_ssl = 0; have_ssl= SHOW_OPTION_DISABLED; } @@ -4300,7 +4318,6 @@ int main(int argc, char **argv) select_thread=pthread_self(); select_thread_in_use=1; - init_ssl(); #ifdef HAVE_LIBWRAP libwrapName= my_progname+dirname_length(my_progname); @@ -4355,6 +4372,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (init_server_components()) unireg_abort(1); + init_ssl(); network_init(); #ifdef __WIN__ @@ -4423,6 +4441,11 @@ we force server id to 2, but this MySQL server will not act as a slave."); unireg_abort(1); } + execute_ddl_log_recovery(); + + if (Events::init(opt_noacl || opt_bootstrap)) + unireg_abort(1); + if (opt_bootstrap) { select_thread_in_use= 0; // Allow 'kill' to work @@ -4434,14 +4457,10 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (read_init_file(opt_init_file)) unireg_abort(1); } - execute_ddl_log_recovery(); create_shutdown_thread(); start_handle_manager(); - if (Events::init(opt_noacl)) - unireg_abort(1); - sql_print_information(ER(ER_STARTUP),my_progname,server_version, ((unix_sock == INVALID_SOCKET) ? (char*) "" : mysqld_unix_port), @@ -4723,6 +4742,7 @@ static void bootstrap(FILE *file) thd->security_ctx->master_access= ~(ulong)0; thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thread_count++; + in_bootstrap= TRUE; bootstrap_file=file; #ifndef EMBEDDED_LIBRARY // TODO: Enable this @@ -4735,7 +4755,7 @@ static void bootstrap(FILE *file) } /* Wait for thread to die */ (void) pthread_mutex_lock(&LOCK_thread_count); - while (thread_count) + while (in_bootstrap) { (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); DBUG_PRINT("quit",("One thread died (count=%u)",thread_count)); @@ -6621,7 +6641,7 @@ The minimum value for this variable is 4096.", "Joins that are probably going to read more than max_join_size records return an error.", (uchar**) &global_system_variables.max_join_size, (uchar**) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG, - ~0L, 1, ~0L, 0, 1, 0}, + HA_POS_ERROR, 1, HA_POS_ERROR, 0, 1, 0}, {"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA, "Max number of bytes in sorted records.", (uchar**) &global_system_variables.max_length_for_sort_data, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0a8720bae64..73892f31ccf 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -186,10 +186,12 @@ my_bool net_realloc(NET *net, size_t length) pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); /* We must allocate some extra bytes for the end 0 and to be able to - read big compressed blocks + read big compressed blocks + 1 safety byte since uint3korr() in + my_real_read() may actually read 4 bytes depending on build flags and + platform. */ if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length + - NET_HEADER_SIZE + COMP_HEADER_SIZE, + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1, MYF(MY_WME)))) { /* @todo: 1 and 2 codes are identical. */ @@ -921,6 +923,13 @@ my_real_read(NET *net, size_t *complen) #ifdef HAVE_COMPRESS if (net->compress) { + /* + The following uint3korr() may read 4 bytes, so make sure we don't + read unallocated or uninitialized memory. The right-hand expression + must match the size of the buffer allocated in net_realloc(). + */ + DBUG_ASSERT(net->where_b + NET_HEADER_SIZE + sizeof(uint32) <= + net->max_packet + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1); /* If the packet is compressed then complen > 0 and contains the number of bytes in the uncompressed packet diff --git a/sql/protocol.cc b/sql/protocol.cc index 16975c68a54..4f69a0fdb52 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -29,11 +29,11 @@ static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; /* Declared non-static only because of the embedded library. */ -void net_send_error_packet(THD *thd, uint sql_errno, const char *err); -void net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *); -void net_send_eof(THD *thd, uint server_status, uint total_warn_count); +bool net_send_error_packet(THD *thd, uint sql_errno, const char *err); +bool net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *); +bool net_send_eof(THD *thd, uint server_status, uint total_warn_count); #ifndef EMBEDDED_LIBRARY -static void write_eof_packet(THD *thd, NET *net, +static bool write_eof_packet(THD *thd, NET *net, uint server_status, uint total_warn_count); #endif @@ -70,8 +70,17 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's critical that every error that can be intercepted is issued in one place only, my_message_sql. + + @param thd Thread handler + @param sql_errno The error code to send + @param err A pointer to the error message + + @return + @retval FALSE The message was sent to the client + @retval TRUE An error occurred and the message wasn't sent properly */ -void net_send_error(THD *thd, uint sql_errno, const char *err) + +bool net_send_error(THD *thd, uint sql_errno, const char *err) { DBUG_ENTER("net_send_error"); @@ -80,6 +89,7 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) DBUG_ASSERT(err && err[0]); DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err)); + bool error; /* It's one case when we can push an error even though there @@ -90,11 +100,11 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) /* Abort multi-result sets */ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; - net_send_error_packet(thd, sql_errno, err); + error= net_send_error_packet(thd, sql_errno, err); thd->main_da.can_overwrite_status= FALSE; - DBUG_VOID_RETURN; + DBUG_RETURN(error); } /** @@ -113,25 +123,33 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) Is not stored if no message. @param thd Thread handler + @param server_status The server status + @param total_warn_count Total number of warnings @param affected_rows Number of rows changed by statement @param id Auto_increment id for first row (if used) @param message Message to send to the client (Used by mysql_status) + + @return + @retval FALSE The message was successfully sent + @retval TRUE An error occurred and the messages wasn't sent properly + */ #ifndef EMBEDDED_LIBRARY -void +bool net_send_ok(THD *thd, uint server_status, uint total_warn_count, ha_rows affected_rows, ulonglong id, const char *message) { NET *net= &thd->net; uchar buff[MYSQL_ERRMSG_SIZE+10],*pos; + bool error= FALSE; DBUG_ENTER("my_ok"); if (! net->vio) // hack for re-parsing queries { DBUG_PRINT("info", ("vio present: NO")); - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } buff[0]=0; // No fields @@ -162,13 +180,14 @@ net_send_ok(THD *thd, if (message && message[0]) pos= net_store_data(pos, (uchar*) message, strlen(message)); - VOID(my_net_write(net, buff, (size_t) (pos-buff))); - VOID(net_flush(net)); + error= my_net_write(net, buff, (size_t) (pos-buff)); + if (!error) + error= net_flush(net); thd->main_da.can_overwrite_status= FALSE; DBUG_PRINT("info", ("OK sent, so no more error sending allowed")); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ @@ -188,37 +207,54 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ client. @param thd Thread handler - @param no_flush Set to 1 if there will be more data to the client, - like in send_fields(). + @param server_status The server status + @param total_warn_count Total number of warnings + + @return + @retval FALSE The message was successfully sent + @retval TRUE An error occurred and the message wasn't sent properly */ -void +bool net_send_eof(THD *thd, uint server_status, uint total_warn_count) { NET *net= &thd->net; + bool error= FALSE; DBUG_ENTER("net_send_eof"); /* Set to TRUE if no active vio, to work well in case of --init-file */ if (net->vio != 0) { thd->main_da.can_overwrite_status= TRUE; - write_eof_packet(thd, net, server_status, total_warn_count); - VOID(net_flush(net)); + error= write_eof_packet(thd, net, server_status, total_warn_count); + if (!error) + error= net_flush(net); thd->main_da.can_overwrite_status= FALSE; DBUG_PRINT("info", ("EOF sent, so no more error sending allowed")); } - DBUG_VOID_RETURN; + DBUG_RETURN(error); } /** Format EOF packet according to the current protocol and write it to the network output buffer. + + @param thd The thread handler + @param net The network handler + @param server_status The server status + @param total_warn_count The number of warnings + + + @return + @retval FALSE The message was sent successfully + @retval TRUE An error occurred and the messages wasn't sent properly */ -static void write_eof_packet(THD *thd, NET *net, +static bool write_eof_packet(THD *thd, NET *net, uint server_status, uint total_warn_count) { + bool error; if (thd->client_capabilities & CLIENT_PROTOCOL_41) { uchar buff[5]; @@ -237,10 +273,12 @@ static void write_eof_packet(THD *thd, NET *net, if (thd->is_fatal_error) server_status&= ~SERVER_MORE_RESULTS_EXISTS; int2store(buff + 3, server_status); - VOID(my_net_write(net, buff, 5)); + error= my_net_write(net, buff, 5); } else - VOID(my_net_write(net, eof_buff, 1)); + error= my_net_write(net, eof_buff, 1); + + return error; } /** @@ -261,7 +299,17 @@ bool send_old_password_request(THD *thd) } -void net_send_error_packet(THD *thd, uint sql_errno, const char *err) +/** + @param thd Thread handler + @param sql_errno The error code to send + @param err A pointer to the error message + + @return + @retval FALSE The message was successfully sent + @retval TRUE An error occurred and the messages wasn't sent properly +*/ + +bool net_send_error_packet(THD *thd, uint sql_errno, const char *err) { NET *net= &thd->net; uint length; @@ -279,7 +327,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err) /* In bootstrap it's ok to print on stderr */ fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); } - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } if (net->return_errno) @@ -301,9 +349,8 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err) length=(uint) strlen(err); set_if_smaller(length,MYSQL_ERRMSG_SIZE-1); } - VOID(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, + DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err, length)); - DBUG_VOID_RETURN; } #endif /* EMBEDDED_LIBRARY */ @@ -389,36 +436,39 @@ void net_end_statement(THD *thd) if (thd->main_da.is_sent) return; + bool error= FALSE; + switch (thd->main_da.status()) { case Diagnostics_area::DA_ERROR: /* The query failed, send error to log and abort bootstrap. */ - net_send_error(thd, - thd->main_da.sql_errno(), - thd->main_da.message()); + error= net_send_error(thd, + thd->main_da.sql_errno(), + thd->main_da.message()); break; case Diagnostics_area::DA_EOF: - net_send_eof(thd, - thd->main_da.server_status(), - thd->main_da.total_warn_count()); + error= net_send_eof(thd, + thd->main_da.server_status(), + thd->main_da.total_warn_count()); break; case Diagnostics_area::DA_OK: - net_send_ok(thd, - thd->main_da.server_status(), - thd->main_da.total_warn_count(), - thd->main_da.affected_rows(), - thd->main_da.last_insert_id(), - thd->main_da.message()); + error= net_send_ok(thd, + thd->main_da.server_status(), + thd->main_da.total_warn_count(), + thd->main_da.affected_rows(), + thd->main_da.last_insert_id(), + thd->main_da.message()); break; case Diagnostics_area::DA_DISABLED: break; case Diagnostics_area::DA_EMPTY: default: DBUG_ASSERT(0); - net_send_ok(thd, thd->server_status, thd->total_warn_count, - 0, 0, NULL); + error= net_send_ok(thd, thd->server_status, thd->total_warn_count, + 0, 0, NULL); break; } - thd->main_da.is_sent= TRUE; + if (!error) + thd->main_da.is_sent= TRUE; } diff --git a/sql/protocol.h b/sql/protocol.h index a4770e9b6e3..251ba6fbc33 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -173,7 +173,7 @@ public: }; void send_warning(THD *thd, uint sql_errno, const char *err=0); -void net_send_error(THD *thd, uint sql_errno=0, const char *err=0); +bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0); void net_end_statement(THD *thd); bool send_old_password_request(THD *thd); uchar *net_store_data(uchar *to,const uchar *from, size_t length); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 45c0efb10c2..18fbae9bb9d 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -947,6 +947,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, if (count_relay_log_space(rli)) { *errmsg= "Error counting relay log space"; + error=1; goto err; } if (!just_reset) diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 42bca02984d..5531ee71620 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6201,3 +6201,8 @@ ER_TEMPORARY_NAME ER_RENAMED_NAME eng "Renamed" swe "Namnändrad" +ER_TOO_MANY_CONCURRENT_TRXS + eng "Too many active concurrent transactions" + +WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED + eng "Non-ASCII separator arguments are not fully supported" diff --git a/sql/slave.cc b/sql/slave.cc index 81be7064f89..3b64e23ece5 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -38,6 +38,7 @@ #include <my_dir.h> #include <sql_common.h> #include <errmsg.h> +#include <mysqld_error.h> #include <mysys_err.h> #ifdef HAVE_REPLICATION @@ -511,7 +512,7 @@ terminate_slave_thread(THD *thd, int error; DBUG_PRINT("loop", ("killing slave thread")); - pthread_mutex_lock(&thd->LOCK_delete); + pthread_mutex_lock(&thd->LOCK_thd_data); #ifndef DONT_USE_THR_ALARM /* Error codes from pthread_kill are: @@ -522,7 +523,7 @@ terminate_slave_thread(THD *thd, DBUG_ASSERT(err != EINVAL); #endif thd->awake(THD::NOT_KILLED); - pthread_mutex_unlock(&thd->LOCK_delete); + pthread_mutex_unlock(&thd->LOCK_thd_data); /* There is a small chance that slave thread might miss the first @@ -859,6 +860,29 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) DBUG_RETURN(1); } + +/* + Check if the error is caused by network. + @param[in] errorno Number of the error. + RETURNS: + TRUE network error + FALSE not network error +*/ + +bool is_network_error(uint errorno) +{ + if (errorno == CR_CONNECTION_ERROR || + errorno == CR_CONN_HOST_ERROR || + errorno == CR_SERVER_GONE_ERROR || + errorno == CR_SERVER_LOST || + errorno == ER_CON_COUNT_ERROR || + errorno == ER_SERVER_SHUTDOWN) + return TRUE; + + return FALSE; +} + + /* Note that we rely on the master's version (3.23, 4.0.14 etc) instead of relying on the binlog's version. This is not perfect: imagine an upgrade @@ -871,6 +895,7 @@ int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) RETURNS 0 ok 1 error + 2 transient network problem, the caller should try to reconnect */ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) @@ -956,6 +981,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) unavailable (very old master not supporting UNIX_TIMESTAMP()?). */ + DBUG_SYNC_POINT("debug_lock.before_get_UNIX_TIMESTAMP", 10); + master_res= NULL; if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) && (master_res= mysql_store_result(mysql)) && (master_row= mysql_fetch_row(master_res))) @@ -963,7 +990,13 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) mi->clock_diff_with_master= (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10)); } - else if (!check_io_slave_killed(mi->io_thd, mi, NULL)) + else if (is_network_error(mysql_errno(mysql))) + { + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "Get master clock failed with error: %s", mysql_error(mysql)); + goto network_err; + } + else { mi->clock_diff_with_master= 0; /* The "most sensible" value */ sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, " @@ -972,7 +1005,10 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) mysql_error(mysql), mysql_errno(mysql)); } if (master_res) + { mysql_free_result(master_res); + master_res= NULL; + } /* Check that the master's server id and ours are different. Because if they @@ -984,12 +1020,15 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) Note: we could have put a @@SERVER_ID in the previous SELECT UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters. */ + DBUG_SYNC_POINT("debug_lock.before_get_SERVER_ID", 10); + master_res= NULL; + master_row= NULL; if (!mysql_real_query(mysql, STRING_WITH_LEN("SHOW VARIABLES LIKE 'SERVER_ID'")) && - (master_res= mysql_store_result(mysql))) + (master_res= mysql_store_result(mysql)) && + (master_row= mysql_fetch_row(master_res))) { - if ((master_row= mysql_fetch_row(master_res)) && - (::server_id == strtoul(master_row[1], 0, 10)) && + if ((::server_id == strtoul(master_row[1], 0, 10)) && !mi->rli.replicate_same_server_id) { errmsg= "The slave I/O thread stops because master and slave have equal \ @@ -998,10 +1037,34 @@ the --replicate-same-server-id option must be used on slave but this does \ not always make sense; please check the manual before using it)."; err_code= ER_SLAVE_FATAL_ERROR; sprintf(err_buff, ER(err_code), errmsg); + goto err; } + } + else if (mysql_errno(mysql)) + { + if (is_network_error(mysql_errno(mysql))) + { + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "Get master SERVER_ID failed with error: %s", mysql_error(mysql)); + goto network_err; + } + /* Fatal error */ + errmsg= "The slave I/O thread stops because a fatal error is encountered \ +when it try to get the value of SERVER_ID variable from master."; + err_code= mysql_errno(mysql); + sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); + goto err; + } + else if (!master_row && master_res) + { + mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE, + "Unknown system variable 'SERVER_ID' on master, \ +maybe it is a *VERY OLD MASTER*."); + } + if (master_res) + { mysql_free_result(master_res); - if (errmsg) - goto err; + master_res= NULL; } /* @@ -1025,23 +1088,50 @@ not always make sense; please check the manual before using it)."; if (*mysql->server_version == '3') goto err; - if ((*mysql->server_version == '4') && - !mysql_real_query(mysql, - STRING_WITH_LEN("SELECT @@GLOBAL.COLLATION_SERVER")) && - (master_res= mysql_store_result(mysql))) + if (*mysql->server_version == '4') { - if ((master_row= mysql_fetch_row(master_res)) && - strcmp(master_row[0], global_system_variables.collation_server->name)) + master_res= NULL; + if (!mysql_real_query(mysql, + STRING_WITH_LEN("SELECT @@GLOBAL.COLLATION_SERVER")) && + (master_res= mysql_store_result(mysql)) && + (master_row= mysql_fetch_row(master_res))) { - errmsg= "The slave I/O thread stops because master and slave have \ + if (strcmp(master_row[0], global_system_variables.collation_server->name)) + { + errmsg= "The slave I/O thread stops because master and slave have \ different values for the COLLATION_SERVER global variable. The values must \ -be equal for replication to work"; - err_code= ER_SLAVE_FATAL_ERROR; - sprintf(err_buff, ER(err_code), errmsg); +be equal for the Statement-format replication to work"; + err_code= ER_SLAVE_FATAL_ERROR; + sprintf(err_buff, ER(err_code), errmsg); + goto err; + } } - mysql_free_result(master_res); - if (errmsg) + else if (is_network_error(mysql_errno(mysql))) + { + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "Get master COLLATION_SERVER failed with error: %s", mysql_error(mysql)); + goto network_err; + } + else if (mysql_errno(mysql) != ER_UNKNOWN_SYSTEM_VARIABLE) + { + /* Fatal error */ + errmsg= "The slave I/O thread stops because a fatal error is encountered \ +when it try to get the value of COLLATION_SERVER global variable from master."; + err_code= mysql_errno(mysql); + sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); goto err; + } + else + mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE, + "Unknown system variable 'COLLATION_SERVER' on master, \ +maybe it is a *VERY OLD MASTER*. *NOTE*: slave may experience \ +inconsistency if replicated data deals with collation."); + + if (master_res) + { + mysql_free_result(master_res); + master_res= NULL; + } } /* @@ -1059,35 +1149,62 @@ be equal for replication to work"; This check is only necessary for 4.x masters (and < 5.0.4 masters but those were alpha). */ - if ((*mysql->server_version == '4') && - !mysql_real_query(mysql, STRING_WITH_LEN("SELECT @@GLOBAL.TIME_ZONE")) && - (master_res= mysql_store_result(mysql))) + if (*mysql->server_version == '4') { - if ((master_row= mysql_fetch_row(master_res)) && - strcmp(master_row[0], - global_system_variables.time_zone->get_name()->ptr())) + master_res= NULL; + if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT @@GLOBAL.TIME_ZONE")) && + (master_res= mysql_store_result(mysql)) && + (master_row= mysql_fetch_row(master_res))) { - errmsg= "The slave I/O thread stops because master and slave have \ + if (strcmp(master_row[0], + global_system_variables.time_zone->get_name()->ptr())) + { + errmsg= "The slave I/O thread stops because master and slave have \ different values for the TIME_ZONE global variable. The values must \ -be equal for replication to work"; - err_code= ER_SLAVE_FATAL_ERROR; - sprintf(err_buff, ER(err_code), errmsg); +be equal for the Statement-format replication to work"; + err_code= ER_SLAVE_FATAL_ERROR; + sprintf(err_buff, ER(err_code), errmsg); + goto err; + } } - mysql_free_result(master_res); - - if (errmsg) + else if (is_network_error(mysql_errno(mysql))) + { + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "Get master TIME_ZONE failed with error: %s", mysql_error(mysql)); + goto network_err; + } + else + { + /* Fatal error */ + errmsg= "The slave I/O thread stops because a fatal error is encountered \ +when it try to get the value of TIME_ZONE global variable from master."; + err_code= mysql_errno(mysql); + sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); goto err; + } + if (master_res) + { + mysql_free_result(master_res); + master_res= NULL; + } } err: if (errmsg) { + if (master_res) + mysql_free_result(master_res); DBUG_ASSERT(err_code != 0); mi->report(ERROR_LEVEL, err_code, err_buff); DBUG_RETURN(1); } DBUG_RETURN(0); + +network_err: + if (master_res) + mysql_free_result(master_res); + DBUG_RETURN(2); } /* @@ -1133,15 +1250,13 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, DBUG_RETURN(1); } thd->command = COM_TABLE_DUMP; - thd->query_length= packet_len; - /* Note that we should not set thd->query until the area is initalized */ if (!(query = thd->strmake((char*) net->read_pos, packet_len))) { sql_print_error("create_table_from_dump: out of memory"); my_message(ER_GET_ERRNO, "Out of memory", MYF(0)); DBUG_RETURN(1); } - thd->query= query; + thd->set_query(query, packet_len); thd->is_slave_error = 0; bzero((char*) &tables,sizeof(tables)); @@ -2374,6 +2489,7 @@ pthread_handler_t handle_slave_io(void *arg) char llbuff[22]; uint retry_count; bool suppress_warnings; + int ret; #ifndef DBUG_OFF uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0; #endif @@ -2454,8 +2570,23 @@ connected: mi->slave_running= MYSQL_SLAVE_RUN_CONNECT; thd->slave_net = &mysql->net; thd_proc_info(thd, "Checking master version"); - if (get_master_version_and_clock(mysql, mi)) + ret= get_master_version_and_clock(mysql, mi); + if (ret == 1) + /* Fatal error */ goto err; + + if (ret == 2) + { + if (check_io_slave_killed(mi->io_thd, mi, "Slave I/O thread killed" + "while calling get_master_version_and_clock(...)")) + goto err; + suppress_warnings= FALSE; + /* Try to reconnect because the error was caused by a transient network problem */ + if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_REG])) + goto err; + goto connected; + } if (mi->rli.relay_log.description_event_for_queue->binlog_version > 1) { @@ -2622,10 +2753,8 @@ err: // print the current replication position sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = thd->db = 0; // extra safety - thd->query_length= thd->db_length= 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->set_query(NULL, 0); + thd->reset_db(NULL, 0); if (mysql) { /* @@ -2977,15 +3106,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ must "proactively" clear playgrounds: */ rli->cleanup_context(thd, 1); - VOID(pthread_mutex_lock(&LOCK_thread_count)); /* Some extra safety, which should not been needed (normally, event deletion should already have done these assignments (each event which sets these variables is supposed to set them to 0 before terminating)). */ - thd->query= thd->db= thd->catalog= 0; - thd->query_length= thd->db_length= 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->catalog= 0; + thd->set_query(NULL, 0); + thd->reset_db(NULL, 0); thd_proc_info(thd, "Waiting for slave mutex on exit"); pthread_mutex_lock(&rli->run_lock); /* We need data_lock, at least to wake up any waiting master_pos_wait() */ diff --git a/sql/sp.cc b/sql/sp.cc index 29e228f5e45..4d840f53e2f 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -70,9 +70,6 @@ enum MYSQL_PROC_FIELD_COUNT }; -/* Tells what SP_DEFAULT_ACCESS should be mapped to */ -#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL - /*************************************************************************/ /** @@ -17,6 +17,9 @@ #ifndef _SP_H_ #define _SP_H_ +/* Tells what SP_DEFAULT_ACCESS should be mapped to */ +#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL + // Return codes from sp_create_*, sp_drop_*, and sp_show_*: #define SP_OK 0 #define SP_KEY_NOT_FOUND -1 diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8a8a5b06cc1..0736e5fc2a8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1012,8 +1012,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) else DBUG_RETURN(TRUE); - thd->query= pbuf; - thd->query_length= qbuf.length(); + thd->set_query(pbuf, qbuf.length()); DBUG_RETURN(FALSE); } @@ -1249,7 +1248,7 @@ sp_head::execute(THD *thd) */ if (thd->prelocked_mode == NON_PRELOCKED) thd->user_var_events_alloc= thd->mem_root; - + err_status= i->execute(thd, &ip); if (i->free_list) @@ -2858,14 +2857,13 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) } else *nextp= m_ip+1; - thd->query= query; - thd->query_length= query_length; + thd->set_query(query, query_length); thd->query_name_consts= 0; if (!thd->is_error()) thd->main_da.reset_diagnostics_area(); } - DBUG_RETURN(res); + DBUG_RETURN(res || thd->is_error()); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 88e1620b152..b81070000b3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7375,7 +7375,13 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, /* make * substituting permanent */ SELECT_LEX *select_lex= thd->lex->current_select; select_lex->with_wild= 0; - select_lex->item_list= fields; + /* + The assignment below is translated to memcpy() call (at least on some + platforms). memcpy() expects that source and destination areas do not + overlap. That problem was detected by valgrind. + */ + if (&select_lex->item_list != &fields) + select_lex->item_list= fields; thd->restore_active_arena(arena, &backup); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 48ddb42f0d8..a9c399e15de 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -627,7 +627,7 @@ THD::THD() #ifdef SIGNAL_WITH_VIO_CLOSE active_vio = 0; #endif - pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST); /* Variables with default values */ proc_info="login"; @@ -911,8 +911,8 @@ THD::~THD() THD_CHECK_SENTRY(this); DBUG_ENTER("~THD()"); /* Ensure that no one is using THD */ - pthread_mutex_lock(&LOCK_delete); - pthread_mutex_unlock(&LOCK_delete); + pthread_mutex_lock(&LOCK_thd_data); + pthread_mutex_unlock(&LOCK_thd_data); add_to_status(&global_status_var, &status_var); /* Close connection */ @@ -939,7 +939,7 @@ THD::~THD() free_root(&transaction.mem_root,MYF(0)); #endif mysys_var=0; // Safety (shouldn't be needed) - pthread_mutex_destroy(&LOCK_delete); + pthread_mutex_destroy(&LOCK_thd_data); #ifndef DBUG_OFF dbug_sentry= THD_SENTRY_GONE; #endif @@ -1012,7 +1012,7 @@ void THD::awake(THD::killed_state state_to_set) DBUG_ENTER("THD::awake"); DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); THD_CHECK_SENTRY(this); - safe_mutex_assert_owner(&LOCK_delete); + safe_mutex_assert_owner(&LOCK_thd_data); killed= state_to_set; if (state_to_set != THD::KILL_QUERY) @@ -1409,7 +1409,7 @@ int THD::send_explain_fields(select_result *result) void THD::close_active_vio() { DBUG_ENTER("close_active_vio"); - safe_mutex_assert_owner(&LOCK_delete); + safe_mutex_assert_owner(&LOCK_thd_data); #ifndef EMBEDDED_LIBRARY if (active_vio) { @@ -1790,6 +1790,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN) strmake(path,exchange->file_name,FN_REFLEN-1); + write_cs= exchange->cs ? exchange->cs : &my_charset_bin; + if ((file= create_file(thd, path, exchange, &cache)) < 0) return 1; /* Check if there is any blobs in data */ @@ -1809,6 +1811,31 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) non_string_results= TRUE; } } + if (exchange->escaped->numchars() > 1 || exchange->enclosed->numchars() > 1) + { + my_error(ER_WRONG_FIELD_TERMINATORS, MYF(0)); + return TRUE; + } + if (exchange->escaped->length() > 1 || exchange->enclosed->length() > 1 || + !my_isascii(exchange->escaped->ptr()[0]) || + !my_isascii(exchange->enclosed->ptr()[0]) || + !exchange->field_term->is_ascii() || !exchange->line_term->is_ascii() || + !exchange->line_start->is_ascii()) + { + /* + Current LOAD DATA INFILE recognizes field/line separators "as is" without + converting from client charset to data file charset. So, it is supposed, + that input file of LOAD DATA INFILE consists of data in one charset and + separators in other charset. For the compatibility with that [buggy] + behaviour SELECT INTO OUTFILE implementation has been saved "as is" too, + but the new warning message has been added: + + Non-ASCII separator arguments are not fully supported + */ + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED, + ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED)); + } field_term_length=exchange->field_term->length(); field_term_char= field_term_length ? (int) (uchar) (*exchange->field_term)[0] : INT_MAX; @@ -1858,6 +1885,8 @@ bool select_export::send_data(List<Item> &items) DBUG_ENTER("select_export::send_data"); char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH]; + char cvt_buff[MAX_FIELD_WIDTH]; + String cvt_str(cvt_buff, sizeof(cvt_buff), write_cs); bool space_inited=0; String tmp(buff,sizeof(buff),&my_charset_bin),*res; tmp.length(0); @@ -1881,6 +1910,37 @@ bool select_export::send_data(List<Item> &items) bool enclosed = (exchange->enclosed->length() && (!exchange->opt_enclosed || result_type == STRING_RESULT)); res=item->str_result(&tmp); + if (res && !my_charset_same(write_cs, res->charset()) && + !my_charset_same(write_cs, &my_charset_bin)) + { + const char *well_formed_error_pos; + const char *cannot_convert_error_pos; + const char *from_end_pos; + const char *error_pos; + uint32 bytes; + bytes= well_formed_copy_nchars(write_cs, cvt_buff, sizeof(cvt_buff), + res->charset(), res->ptr(), res->length(), + sizeof(cvt_buff), + &well_formed_error_pos, + &cannot_convert_error_pos, + &from_end_pos); + error_pos= well_formed_error_pos ? well_formed_error_pos + : cannot_convert_error_pos; + if (error_pos) + { + char printable_buff[32]; + convert_to_printable(printable_buff, sizeof(printable_buff), + error_pos, res->ptr() + res->length() - error_pos, + res->charset(), 6); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, + ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + "string", printable_buff, + item->name, row_count); + } + cvt_str.length(bytes); + res= &cvt_str; + } if (res && enclosed) { if (my_b_write(&cache,(uchar*) exchange->enclosed->ptr(), @@ -3055,6 +3115,25 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) } +void THD::set_statement(Statement *stmt) +{ + pthread_mutex_lock(&LOCK_thd_data); + Statement::set_statement(stmt); + pthread_mutex_unlock(&LOCK_thd_data); +} + + +/** Assign a new value to thd->query. */ + +void THD::set_query(char *query_arg, uint32 query_length_arg) +{ + pthread_mutex_lock(&LOCK_thd_data); + query= query_arg; + query_length= query_length_arg; + pthread_mutex_unlock(&LOCK_thd_data); +} + + /** Mark transaction to rollback and mark error as fatal to a sub-statement. @@ -3695,7 +3774,8 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, push_warning(this, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_BINLOG_UNSAFE_STATEMENT, ER(ER_BINLOG_UNSAFE_STATEMENT)); - if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED)) + if (global_system_variables.log_warnings && + !(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED)) { sql_print_warning("%s Statement: %.*s", ER(ER_BINLOG_UNSAFE_STATEMENT), diff --git a/sql/sql_class.h b/sql/sql_class.h index 36e696f2da6..09841edc480 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -631,22 +631,16 @@ public: we need to declare it char * because all table handlers are written in C and need to point to it. - Note that (A) if we set query = NULL, we must at the same time set - query_length = 0, and protect the whole operation with the - LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a - non-NULL value if its previous value is NULL. We do not need to protect - operation (B) with any mutex. To avoid crashes in races, if we do not - know that thd->query cannot change at the moment, one should print + Note that if we set query = NULL, we must at the same time set + query_length = 0, and protect the whole operation with + LOCK_thd_data mutex. To avoid crashes in races, if we do not + know that thd->query cannot change at the moment, we should print thd->query like this: - (1) reserve the LOCK_thread_count mutex; - (2) check if thd->query is NULL; - (3) if not NULL, then print at most thd->query_length characters from - it. We will see the query_length field as either 0, or the right value - for it. - Assuming that the write and read of an n-bit memory field in an n-bit - computer is atomic, we can avoid races in the above way. - This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB - STATUS. + (1) reserve the LOCK_thd_data mutex; + (2) print or copy the value of query and query_length + (3) release LOCK_thd_data mutex. + This printing is needed at least in SHOW PROCESSLIST and SHOW + ENGINE INNODB STATUS. */ char *query; uint32 query_length; // current query length @@ -678,7 +672,7 @@ public: virtual ~Statement(); /* Assign execution context (note: not all members) of given stmt to self */ - void set_statement(Statement *stmt); + virtual void set_statement(Statement *stmt); void set_n_backup_statement(Statement *stmt, Statement *backup); void restore_backup_statement(Statement *stmt, Statement *backup); /* return class type */ @@ -1298,7 +1292,15 @@ public: THR_LOCK_OWNER main_lock_id; // To use for conventional queries THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to // the lock_id of a cursor. - pthread_mutex_t LOCK_delete; // Locked before thd is deleted + /** + Protects THD data accessed from other threads: + - thd->query and thd->query_length (used by SHOW ENGINE + INNODB STATUS and SHOW PROCESSLIST + - thd->mysys_var (used by KILL statement and shutdown). + Is locked when THD is deleted. + */ + pthread_mutex_t LOCK_thd_data; + /* all prepared statements and cursors of this connection */ Statement_map stmt_map; /* @@ -1335,6 +1337,10 @@ public: Set it using the thd_proc_info(THD *thread, const char *message) macro/function. + + This member is accessed and assigned without any synchronization. + Therefore, it may point only to constant (statically + allocated) strings, which memory won't go away over time. */ const char *proc_info; @@ -1886,15 +1892,15 @@ public: #ifdef SIGNAL_WITH_VIO_CLOSE inline void set_active_vio(Vio* vio) { - pthread_mutex_lock(&LOCK_delete); + pthread_mutex_lock(&LOCK_thd_data); active_vio = vio; - pthread_mutex_unlock(&LOCK_delete); + pthread_mutex_unlock(&LOCK_thd_data); } inline void clear_active_vio() { - pthread_mutex_lock(&LOCK_delete); + pthread_mutex_lock(&LOCK_thd_data); active_vio = 0; - pthread_mutex_unlock(&LOCK_delete); + pthread_mutex_unlock(&LOCK_thd_data); } void close_active_vio(); #endif @@ -2267,6 +2273,14 @@ public: */ void pop_internal_handler(); + /** Overloaded to guard query/query_length fields */ + virtual void set_statement(Statement *stmt); + + /** + Assign a new value to thd->query. + Protected with LOCK_thd_data mutex. + */ + void set_query(char *query_arg, uint32 query_length_arg); private: /** The current internal error handler for this thread, or NULL. */ Internal_error_handler *m_internal_handler; @@ -2479,6 +2493,7 @@ class select_export :public select_to_file { */ bool is_unsafe_field_sep; bool fixed_row_size; + CHARSET_INFO *write_cs; // output charset public: select_export(sql_exchange *ex) :select_to_file(ex) {} /** diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 677098d275a..0d4bf3f0b8f 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -187,6 +187,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, delete select; free_underlaid_joins(thd, select_lex); thd->row_count_func= 0; + /* + Error was already created by quick select evaluation (check_quick()). + TODO: Add error code output parameter to Item::val_xxx() methods. + Currently they rely on the user checking DA for + errors when unwinding the stack after calling Item::val_xxx(). + */ + if (thd->is_error()) + DBUG_RETURN(TRUE); my_ok(thd, (ha_rows) thd->row_count_func); /* We don't need to call reset_auto_increment in this case, because @@ -490,7 +498,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) if (select_lex->inner_refs_list.elements && fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); select_lex->fix_prepare_information(thd, conds, &fake_conds); DBUG_RETURN(FALSE); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 16810e29343..1e92d95573a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -252,14 +252,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) /* for now HANDLER can be used only for real TABLES */ tables->required_type= FRMTYPE_TABLE; + /* + We use open_tables() here, rather than, say, + open_ltable() or open_table() because we would like to be able + to open a temporary table. + */ error= open_tables(thd, &tables, &counter, 0); - /* restore the state and merge the opened table into handler_tables list */ if (thd->open_tables) { - thd->open_tables->next= thd->handler_tables; - thd->handler_tables= thd->open_tables; + if (thd->open_tables->next) + { + /* + We opened something that is more than a single table. + This happens with MERGE engine. Don't try to link + this mess into thd->handler_tables list, close it + and report an error. We must do it right away + because mysql_ha_close_table(), called down the road, + can close a single table only. + */ + close_thread_tables(thd); + my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); + error= 1; + } + else + { + /* Merge the opened table into handler_tables list. */ + thd->open_tables->next= thd->handler_tables; + thd->handler_tables= thd->open_tables; + } } + /* Restore the state. */ thd->open_tables= backup_open_tables; if (error) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 104c81ca984..7d581f8c3a9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1909,7 +1909,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) thread_count++; pthread_mutex_unlock(&LOCK_thread_count); di->thd.set_db(table_list->db, (uint) strlen(table_list->db)); - di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME)); + di->thd.set_query(my_strdup(table_list->table_name, MYF(MY_WME)), 0); if (di->thd.db == NULL || di->thd.query == NULL) { /* The error is reported */ diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 1758a6df5f9..b7f33d51335 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -148,6 +148,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(TRUE); } + + /* Report problems with non-ascii separators */ + if (!escaped->is_ascii() || !enclosed->is_ascii() || + !field_term->is_ascii() || + !ex->line_term->is_ascii() || !ex->line_start->is_ascii()) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED, + ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED)); + } + if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 542ce992537..fb5d58b63c4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -457,6 +457,7 @@ pthread_handler_t handle_bootstrap(void *arg) thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { + char *query; /* strlen() can't be deleted because fgets() doesn't return length */ ulong length= (ulong) strlen(buff); while (buff[length-1] != '\n' && !feof(file)) @@ -489,11 +490,10 @@ pthread_handler_t handle_bootstrap(void *arg) if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0) continue; - thd->query_length=length; - thd->query= (char*) thd->memdup_w_gap(buff, length+1, - thd->db_length+1+ - QUERY_CACHE_FLAGS_SIZE); - thd->query[length] = '\0'; + query= (char *) thd->memdup_w_gap(buff, length + 1, + thd->db_length + 1 + + QUERY_CACHE_FLAGS_SIZE); + thd->set_query(query, length); DBUG_PRINT("query",("%-.4096s",thd->query)); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.start_new_query(); @@ -533,8 +533,9 @@ end: #ifndef EMBEDDED_LIBRARY (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; - (void) pthread_mutex_unlock(&LOCK_thread_count); + in_bootstrap= FALSE; (void) pthread_cond_broadcast(&COND_thread_count); + (void) pthread_mutex_unlock(&LOCK_thread_count); my_thread_end(); pthread_exit(0); #endif @@ -658,8 +659,7 @@ int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name) if (check_one_table_access(thd, SELECT_ACL, table_list)) goto err; thd->free_list = 0; - thd->query_length=(uint) strlen(tbl_name); - thd->query = tbl_name; + thd->set_query(tbl_name, (uint) strlen(tbl_name)); if ((error = mysqld_dump_create_info(thd, table_list, -1))) { my_error(ER_GET_ERRNO, MYF(0), my_errno); @@ -1239,9 +1239,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->profiling.set_query_source(beginning_of_next_stmt, length); #endif + thd->set_query(beginning_of_next_stmt, length); VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query_length= length; - thd->query= beginning_of_next_stmt; /* Count each statement from the client. */ @@ -1294,9 +1293,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, table_list.schema_table= schema_table; } - thd->query_length= (uint) (packet_end - packet); // Don't count end \0 - if (!(thd->query=fields= (char*) thd->memdup(packet,thd->query_length+1))) + uint query_length= (uint) (packet_end - packet); // Don't count end \0 + if (!(fields= (char *) thd->memdup(packet, query_length + 1))) break; + thd->set_query(fields, query_length); general_log_print(thd, command, "%s %s", table_list.table_name, fields); if (lower_case_table_names) my_casedn_str(files_charset_info, table_list.table_name); @@ -1589,13 +1589,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, log_slow_statement(thd); thd_proc_info(thd, "cleaning up"); - VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list - thd_proc_info(thd, 0); + thd->set_query(NULL, 0); thd->command=COM_SLEEP; - thd->query=0; - thd->query_length=0; + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list thread_running--; VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd_proc_info(thd, 0); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); DBUG_RETURN(error); @@ -1788,6 +1787,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, bool alloc_query(THD *thd, const char *packet, uint packet_length) { + char *query; /* Remove garbage at start and end of query */ while (packet_length > 0 && my_isspace(thd->charset(), packet[0])) { @@ -1802,14 +1802,13 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) packet_length--; } /* We must allocate some extra memory for query cache */ - thd->query_length= 0; // Extra safety: Avoid races - if (!(thd->query= (char*) thd->memdup_w_gap((uchar*) (packet), - packet_length, - thd->db_length+ 1 + - QUERY_CACHE_FLAGS_SIZE))) - return TRUE; - thd->query[packet_length]=0; - thd->query_length= packet_length; + if (! (query= (char*) thd->memdup_w_gap(packet, + packet_length, + 1 + thd->db_length + + QUERY_CACHE_FLAGS_SIZE))) + return TRUE; + query[packet_length]= '\0'; + thd->set_query(query, packet_length); /* Reclaim some memory */ thd->packet.shrink(thd->variables.net_buffer_length); @@ -6950,7 +6949,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) continue; if (tmp->thread_id == id) { - pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete + pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete break; } } @@ -6983,7 +6982,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) } else error=ER_KILL_DENIED_ERROR; - pthread_mutex_unlock(&tmp->LOCK_delete); + pthread_mutex_unlock(&tmp->LOCK_thd_data); } DBUG_PRINT("exit", ("%d", error)); DBUG_RETURN(error); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index e7e821dd4ae..c1839b7220f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -127,12 +127,12 @@ class Prepared_statement: public Statement public: enum flag_values { - IS_IN_USE= 1 + IS_IN_USE= 1, + IS_SQL_PREPARE= 2 }; THD *thd; Select_fetch_protocol_binary result; - Protocol *protocol; Item_param **param_array; uint param_count; uint last_errno; @@ -148,7 +148,7 @@ public: List<LEX_STRING>& varnames, String *expanded_query); public: - Prepared_statement(THD *thd_arg, Protocol *protocol_arg); + Prepared_statement(THD *thd_arg); virtual ~Prepared_statement(); void setup_set_params(); virtual Query_arena::Type type() const; @@ -156,7 +156,8 @@ public: bool set_name(LEX_STRING *name); inline void close_cursor() { delete cursor; cursor= 0; } inline bool is_in_use() { return flags & (uint) IS_IN_USE; } - inline bool is_protocol_text() const { return protocol == &thd->protocol_text; } + inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; } + void set_sql_prepare() { flags|= (uint) IS_SQL_PREPARE; } bool prepare(const char *packet, uint packet_length); bool execute_loop(String *expanded_query, bool open_cursor, @@ -1358,7 +1359,7 @@ static int mysql_test_select(Prepared_statement *stmt, */ if (unit->prepare(thd, 0, 0)) goto error; - if (!lex->describe && !stmt->is_protocol_text()) + if (!lex->describe && !stmt->is_sql_prepare()) { /* Make copy of item list, as change_columns may change it */ List<Item> fields(lex->select_lex.item_list); @@ -1988,7 +1989,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; } if (res == 0) - DBUG_RETURN(stmt->is_protocol_text() ? + DBUG_RETURN(stmt->is_sql_prepare() ? FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush())); error: DBUG_RETURN(TRUE); @@ -2058,6 +2059,7 @@ static bool init_param_array(Prepared_statement *stmt) void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) { + Protocol *save_protocol= thd->protocol; Prepared_statement *stmt; bool error; DBUG_ENTER("mysqld_stmt_prepare"); @@ -2067,7 +2069,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) /* First of all clear possible warnings from the previous command */ mysql_reset_thd_for_next_command(thd); - if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary))) + if (! (stmt= new Prepared_statement(thd))) DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ if (thd->stmt_map.insert(thd, stmt)) @@ -2084,6 +2086,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); + thd->protocol= &thd->protocol_binary; + if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -2097,6 +2101,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); } + + thd->protocol= save_protocol; + /* check_prepared_statemnt sends the metadata packet in case of success */ DBUG_VOID_RETURN; } @@ -2229,7 +2236,6 @@ void mysql_sql_stmt_prepare(THD *thd) const char *query; uint query_len= 0; DBUG_ENTER("mysql_sql_stmt_prepare"); - DBUG_ASSERT(thd->protocol == &thd->protocol_text); if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) { @@ -2247,11 +2253,13 @@ void mysql_sql_stmt_prepare(THD *thd) } if (! (query= get_dynamic_sql_string(lex, &query_len)) || - ! (stmt= new Prepared_statement(thd, &thd->protocol_text))) + ! (stmt= new Prepared_statement(thd))) { DBUG_VOID_RETURN; /* out of memory */ } + stmt->set_sql_prepare(); + /* Set the name first, insert should know that this statement has a name */ if (stmt->set_name(name)) { @@ -2431,6 +2439,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) String expanded_query; uchar *packet_end= packet + packet_length; Prepared_statement *stmt; + Protocol *save_protocol= thd->protocol; bool open_cursor; DBUG_ENTER("mysqld_stmt_execute"); @@ -2458,7 +2467,9 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY); + thd->protocol= &thd->protocol_binary; stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end); + thd->protocol= save_protocol; /* Close connection socket; for use with client testing (Bug#43560). */ DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio);); @@ -2814,12 +2825,11 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields) Prepared_statement ****************************************************************************/ -Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) +Prepared_statement::Prepared_statement(THD *thd_arg) :Statement(NULL, &main_mem_root, INITIALIZED, ++thd_arg->statement_id_counter), thd(thd_arg), result(thd_arg), - protocol(protocol_arg), param_array(0), param_count(0), last_errno(0), @@ -3288,7 +3298,9 @@ Prepared_statement::reprepare() bool cur_db_changed; bool error; - Prepared_statement copy(thd, &thd->protocol_text); + Prepared_statement copy(thd); + + copy.set_sql_prepare(); /* To suppress sending metadata to the client. */ status_var_increment(thd->status_var.com_stmt_reprepare); @@ -3346,7 +3358,7 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy) return FALSE -- the metadata of the original SELECT, if any, has not been sent to the client. */ - if (is_protocol_text() || lex->describe) + if (is_sql_prepare() || lex->describe) return FALSE; if (lex->select_lex.item_list.elements != @@ -3409,7 +3421,6 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) DBUG_ASSERT(thd == copy->thd); last_error[0]= '\0'; last_errno= 0; - /* Do not swap protocols, the copy always has protocol_text */ } @@ -3550,8 +3561,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) thd->stmt_arena= this; reinit_stmt_before_use(thd, lex); - thd->protocol= protocol; /* activate stmt protocol */ - /* Go! */ if (open_cursor) @@ -3582,8 +3591,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (cur_db_changed) mysql_change_db(thd, &saved_cur_db_name, TRUE); - thd->protocol= &thd->protocol_text; /* use normal protocol */ - /* Assert that if an error, no cursor is open */ DBUG_ASSERT(! (error && cursor)); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 476c8aaaefb..0ec8d91214c 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1102,7 +1102,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) if (tmp->command == COM_BINLOG_DUMP && tmp->server_id == slave_server_id) { - pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete + pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete break; } } @@ -1115,7 +1115,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) again. We just to do kill the thread ourselves. */ tmp->awake(THD::KILL_QUERY); - pthread_mutex_unlock(&tmp->LOCK_delete); + pthread_mutex_unlock(&tmp->LOCK_thd_data); } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 37721660184..c900588212c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1807,7 +1807,8 @@ JOIN::exec() curr_join->having= curr_join->tmp_having= 0; // Allready done /* Change sum_fields reference to calculated fields in tmp_table */ - curr_join->all_fields= *curr_all_fields; + if (curr_join != this) + curr_join->all_fields= *curr_all_fields; if (!items1) { items1= items0 + all_fields.elements; @@ -1826,8 +1827,11 @@ JOIN::exec() fields_list.elements, all_fields)) DBUG_VOID_RETURN; } - curr_join->tmp_all_fields1= tmp_all_fields1; - curr_join->tmp_fields_list1= tmp_fields_list1; + if (curr_join != this) + { + curr_join->tmp_all_fields1= tmp_all_fields1; + curr_join->tmp_fields_list1= tmp_fields_list1; + } curr_join->items1= items1; } curr_all_fields= &tmp_all_fields1; @@ -1975,8 +1979,11 @@ JOIN::exec() tmp_fields_list2, tmp_all_fields2, fields_list.elements, tmp_all_fields1)) DBUG_VOID_RETURN; - curr_join->tmp_fields_list2= tmp_fields_list2; - curr_join->tmp_all_fields2= tmp_all_fields2; + if (curr_join != this) + { + curr_join->tmp_fields_list2= tmp_fields_list2; + curr_join->tmp_all_fields2= tmp_all_fields2; + } } curr_fields_list= &curr_join->tmp_fields_list2; curr_all_fields= &curr_join->tmp_all_fields2; @@ -2031,8 +2038,11 @@ JOIN::exec() tmp_table_param.save_copy_field= curr_join->tmp_table_param.copy_field; tmp_table_param.save_copy_field_end= curr_join->tmp_table_param.copy_field_end; - curr_join->tmp_all_fields3= tmp_all_fields3; - curr_join->tmp_fields_list3= tmp_fields_list3; + if (curr_join != this) + { + curr_join->tmp_all_fields3= tmp_all_fields3; + curr_join->tmp_fields_list3= tmp_fields_list3; + } } else { @@ -10652,6 +10662,11 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, if (table->s->db_type() != heap_hton || error != HA_ERR_RECORD_FILE_FULL) { + /* + We don't want this error to be converted to a warning, e.g. in case of + INSERT IGNORE ... SELECT. + */ + thd->fatal_error(); table->file->print_error(error,MYF(0)); DBUG_RETURN(1); } @@ -13132,9 +13147,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, for (nr=0; nr < table->s->keys ; nr++) { int direction; + if (keys.is_set(nr) && (direction= test_if_order_by_key(order, table, nr, &used_key_parts))) { + /* + At this point we are sure that ref_key is a non-ordering + key (where "ordering key" is a key that will return rows + in the order required by ORDER BY). + */ + DBUG_ASSERT (ref_key != (int) nr); + bool is_covering= table->covering_keys.is_set(nr) || (nr == table->s->primary_key && table->file->primary_key_is_clustered()); @@ -13215,7 +13238,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ index_scan_time= select_limit/rec_per_key * min(rec_per_key, table->file->scan_time()); - if (is_covering || + if ((ref_key < 0 && is_covering) || (ref_key < 0 && (group || table->force_index)) || index_scan_time < read_time) { diff --git a/sql/sql_select.h b/sql/sql_select.h index 353c2b1d610..a0366d47149 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -466,7 +466,8 @@ public: group_optimized_away= 0; all_fields= fields_arg; - fields_list= fields_arg; + if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ + fields_list= fields_arg; bzero((char*) &keyuse,sizeof(keyuse)); tmp_table_param.init(); tmp_table_param.end_write_records= HA_POS_ERROR; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d07e951bfd1..ae75609e2b6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -74,9 +74,6 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), grant_names, NULL}; #endif -/* Match the values of enum ha_choice */ -static const char *ha_choice_values[] = {"", "0", "1"}; - static void store_key_options(THD *thd, String *packet, TABLE *table, KEY *key_info); @@ -1151,7 +1148,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, { const LEX_STRING *const db= table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db; - if (strcmp(db->str, thd->db) != 0) + if (!thd->db || strcmp(db->str, thd->db)) { append_identifier(thd, packet, db->str, db->length); packet->append(STRING_WITH_LEN(".")); @@ -1428,11 +1425,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ if (share->db_create_options & HA_OPTION_CHECKSUM) packet->append(STRING_WITH_LEN(" CHECKSUM=1")); - if (share->page_checksum != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); - packet->append(ha_choice_values[(uint) share->page_checksum], 1); - } if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); if (create_info.row_type != ROW_TYPE_DEFAULT) @@ -1440,11 +1432,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); packet->append(ha_row_type[(uint) create_info.row_type]); } - if (share->transactional != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); - packet->append(ha_choice_values[(uint) share->transactional], 1); - } if (table->s->key_block_size) { char *end; @@ -1768,16 +1755,14 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thd_info->start_time= tmp->start_time; thd_info->query=0; + /* Lock THD mutex that protects its data when looking at it. */ + pthread_mutex_lock(&tmp->LOCK_thd_data); if (tmp->query) { - /* - query_length is always set to 0 when we set query = NULL; see - the comment in sql_class.h why this prevents crashes in possible - races with query_length - */ uint length= min(max_query_length, tmp->query_length); thd_info->query=(char*) thd->strmake(tmp->query,length); } + pthread_mutex_unlock(&tmp->LOCK_thd_data); thread_infos.append(thd_info); } } @@ -3593,21 +3578,12 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ if (share->db_create_options & HA_OPTION_CHECKSUM) ptr=strmov(ptr," checksum=1"); - if (share->page_checksum != HA_CHOICE_UNDEF) - ptr= strxmov(ptr, " page_checksum=", - ha_choice_values[(uint) share->page_checksum], NullS); if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) ptr=strmov(ptr," delay_key_write=1"); if (share->row_type != ROW_TYPE_DEFAULT) ptr=strxmov(ptr, " row_format=", ha_row_type[(uint) share->row_type], NullS); - if (share->transactional != HA_CHOICE_UNDEF) - { - ptr= strxmov(ptr, " TRANSACTIONAL=", - (share->transactional == HA_CHOICE_YES ? "1" : "0"), - NullS); - } if (share->key_block_size) { ptr= strmov(ptr, " KEY_BLOCK_SIZE="); @@ -3617,9 +3593,6 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, if (is_partitioned) ptr= strmov(ptr, " partitioned"); #endif - if (share->transactional != HA_CHOICE_UNDEF) - ptr= strxmov(ptr, " transactional=", - ha_choice_values[(uint) share->transactional], NullS); table->field[19]->store(option_buff+1, (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1), cs); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 7759985ba85..7c9793b273b 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1119,3 +1119,76 @@ void String::swap(String &s) swap_variables(bool, alloced, s.alloced); swap_variables(CHARSET_INFO*, str_charset, s.str_charset); } + + +/** + Convert string to printable ASCII string + + @details This function converts input string "from" replacing non-ASCII bytes + with hexadecimal sequences ("\xXX") optionally appending "..." to the end of + the resulting string. + This function used in the ER_TRUNCATED_WRONG_VALUE_FOR_FIELD error messages, + e.g. when a string cannot be converted to a result charset. + + + @param to output buffer + @param to_len size of the output buffer (8 bytes or greater) + @param from input string + @param from_len size of the input string + @param from_cs input charset + @param nbytes maximal number of bytes to convert (from_len if 0) + + @return number of bytes in the output string +*/ + +uint convert_to_printable(char *to, size_t to_len, + const char *from, size_t from_len, + CHARSET_INFO *from_cs, size_t nbytes /*= 0*/) +{ + /* needs at least 8 bytes for '\xXX...' and zero byte */ + DBUG_ASSERT(to_len >= 8); + + char *t= to; + char *t_end= to + to_len - 1; // '- 1' is for the '\0' at the end + const char *f= from; + const char *f_end= from + (nbytes ? min(from_len, nbytes) : from_len); + char *dots= to; // last safe place to append '...' + + if (!f || t == t_end) + return 0; + + for (; t < t_end && f < f_end; f++) + { + /* + If the source string is ASCII compatible (mbminlen==1) + and the source character is in ASCII printable range (0x20..0x7F), + then display the character as is. + + Otherwise, if the source string is not ASCII compatible (e.g. UCS2), + or the source character is not in the printable range, + then print the character using HEX notation. + */ + if (((unsigned char) *f) >= 0x20 && + ((unsigned char) *f) <= 0x7F && + from_cs->mbminlen == 1) + { + *t++= *f; + } + else + { + if (t_end - t < 4) // \xXX + break; + *t++= '\\'; + *t++= 'x'; + *t++= _dig_vec_upper[((unsigned char) *f) >> 4]; + *t++= _dig_vec_upper[((unsigned char) *f) & 0x0F]; + } + if (t_end - t >= 3) // '...' + dots= t; + } + if (f < from + from_len) + memcpy(dots, STRING_WITH_LEN("...\0")); + else + *t= '\0'; + return t - to; +} diff --git a/sql/sql_string.h b/sql/sql_string.h index be11fea70dc..d62908e5d66 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -40,6 +40,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs, size_t my_copy_with_hex_escaping(CHARSET_INFO *cs, char *dst, size_t dstlen, const char *src, size_t srclen); +uint convert_to_printable(char *to, size_t to_len, + const char *from, size_t from_len, + CHARSET_INFO *from_cs, size_t nbytes= 0); class String { @@ -366,6 +369,19 @@ public: { return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length); } + bool is_ascii() const + { + if (length() == 0) + return TRUE; + if (charset()->mbminlen > 1) + return FALSE; + for (const char *c= ptr(), *end= c + length(); c < end; c++) + { + if (!my_isascii(*c)) + return FALSE; + } + return TRUE; + } }; static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e752421223a..0066c66eb59 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1799,6 +1799,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, int non_temp_tables_count= 0; bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0; String built_query; + String built_tmp_query; DBUG_ENTER("mysql_rm_table_part2"); LINT_INIT(alias); @@ -1866,6 +1867,25 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, case 0: // removed temporary table tmp_table_deleted= 1; + if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && + thd->current_stmt_binlog_row_based) + { + if (built_tmp_query.is_empty()) + { + built_tmp_query.set_charset(system_charset_info); + built_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); + } + + built_tmp_query.append("`"); + if (thd->db == NULL || strcmp(db,thd->db) != 0) + { + built_tmp_query.append(db); + built_tmp_query.append("`.`"); + } + built_tmp_query.append(table->table_name); + built_tmp_query.append("`,"); + } + continue; case -1: DBUG_ASSERT(thd->in_sub_stmt); @@ -2023,29 +2043,52 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, write_bin_log(thd, !error, thd->query, thd->query_length); } else if (thd->current_stmt_binlog_row_based && - non_temp_tables_count > 0 && tmp_table_deleted) { + if (non_temp_tables_count > 0) + { + /* + In this case we have deleted both temporary and + non-temporary tables, so: + - since we have deleted a non-temporary table we have to + binlog the statement, but + - since we have deleted a temporary table we cannot binlog + the statement (since the table may have not been created on the + slave - check "if" branch below, this might cause the slave to + stop). + + Instead, we write a built statement, only containing the + non-temporary tables, to the binary log + */ + built_query.chop(); // Chop of the last comma + built_query.append(" /* generated by server */"); + write_bin_log(thd, !error, built_query.ptr(), built_query.length()); + } + /* - In this case we have deleted both temporary and - non-temporary tables, so: - - since we have deleted a non-temporary table we have to - binlog the statement, but - - since we have deleted a temporary table we cannot binlog - the statement (since the table has not been created on the - slave, this might cause the slave to stop). - - Instead, we write a built statement, only containing the - non-temporary tables, to the binary log + One needs to always log any temporary table drop, if: + 1. thread logging format is mixed mode; AND + 2. current statement logging format is set to row. */ - built_query.chop(); // Chop of the last comma - built_query.append(" /* generated by server */"); - write_bin_log(thd, !error, built_query.ptr(), built_query.length()); + if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED) + { + /* + In this case we have deleted some temporary tables but we are using + row based logging for the statement. However, thread uses mixed mode + format, thence we need to log the dropping as we cannot tell for + sure whether the create was logged as statement previously or not, ie, + before switching to row mode. + */ + built_tmp_query.chop(); // Chop of the last comma + built_tmp_query.append(" /* generated by server */"); + write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length()); + } } + /* The remaining cases are: - - no tables where deleted and - - only temporary tables where deleted and row-based + - no tables were deleted and + - only temporary tables were deleted and row-based replication is used. In both these cases, nothing should be written to the binary log. @@ -5885,8 +5928,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE)) create_info->key_block_size= table->s->key_block_size; - if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL)) - create_info->transactional= table->s->transactional; if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 17cc683e14a..6884f863326 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -292,7 +292,7 @@ int mysql_update(THD *thd, if (select_lex->inner_refs_list.elements && fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(-1); + DBUG_RETURN(1); if (conds) { @@ -332,7 +332,14 @@ int mysql_update(THD *thd, { delete select; free_underlaid_joins(thd, select_lex); - if (error) + /* + There was an error or the error was already sent by + the quick select evaluation. + TODO: Add error code output parameter to Item::val_xxx() methods. + Currently they rely on the user checking DA for + errors when unwinding the stack after calling Item::val_xxx(). + */ + if (error || thd->is_error()) { DBUG_RETURN(1); // Error in where } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 57a828a690b..320b43c8e5c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -47,6 +47,12 @@ #include <myisam.h> #include <myisammrg.h> +/* this is to get the bison compilation windows warnings out */ +#ifdef _MSC_VER +/* warning C4065: switch statement contains 'default' but no 'case' labels */ +#pragma warning (disable : 4065) +#endif + int yylex(void *yylval, void *yythd); const LEX_STRING null_lex_str= {0,0}; @@ -883,7 +889,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token OWNER_SYM %token PACK_KEYS_SYM %token PAGE_SYM -%token PAGE_CHECKSUM_SYM %token PARAM_MARKER %token PARSER_SYM %token PARTIAL /* SQL-2003-N */ @@ -1042,7 +1047,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TO_SYM /* SQL-2003-R */ %token TRAILING /* SQL-2003-R */ %token TRANSACTION_SYM -%token TRANSACTIONAL_SYM %token TRIGGERS_SYM %token TRIGGER_SYM /* SQL-2003-R */ %token TRIM /* SQL-2003-N */ @@ -4202,6 +4206,10 @@ opt_sub_partition: if (Lex->part_info->no_subparts != 0 && !Lex->part_info->use_default_subpartitions) { + /* + We come here when we have defined subpartitions on the first + partition but not on all the subsequent partitions. + */ my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); MYSQL_YYABORT; } @@ -4244,6 +4252,23 @@ sub_part_definition: partition_info *part_info= lex->part_info; partition_element *curr_part= part_info->current_partition; partition_element *sub_p_elem= new partition_element(curr_part); + if (part_info->use_default_subpartitions && + part_info->partitions.elements >= 2) + { + /* + create table t1 (a int) + partition by list (a) subpartition by hash (a) + (partition p0 values in (1), + partition p1 values in (2) subpartition sp11); + causes use to arrive since we are on the second + partition, but still use_default_subpartitions + is set. When we come here we're processing at least + the second partition (the current partition processed + have already been put into the partitions list. + */ + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; + } if (!sub_p_elem || curr_part->subpartitions.push_back(sub_p_elem)) { @@ -4460,11 +4485,6 @@ create_table_option: Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } - | PAGE_CHECKSUM_SYM opt_equal choice - { - Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; - Lex->create_info.page_checksum= $3; - } | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; @@ -4524,11 +4544,6 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; Lex->create_info.key_block_size= $3; } - | TRANSACTIONAL_SYM opt_equal choice - { - Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; - Lex->create_info.transactional= $3; - } ; default_charset: @@ -4610,7 +4625,6 @@ row_types: | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } | COMPACT_SYM { $$= ROW_TYPE_COMPACT; } - | PAGE_SYM { $$= ROW_TYPE_PAGE; } ; merge_insert_types: @@ -8493,6 +8507,7 @@ table_factor: MYSQL_YYABORT; sel->add_joined_table($$); lex->pop_context(); + lex->nest_level--; } else if ($4 || $6) { @@ -8501,7 +8516,11 @@ table_factor: MYSQL_YYABORT; } else + { + /* nested join: FROM (t1 JOIN t2 ...), + nest_level is the same as in the outer query */ $$= $3; + } } ; @@ -9194,6 +9213,8 @@ into_destination: !(lex->result= new select_export(lex->exchange, lex->nest_level))) MYSQL_YYABORT; } + opt_load_data_charset + { Lex->exchange->cs= $4; } opt_field_term opt_line_term | DUMPFILE TEXT_STRING_filesystem { @@ -11548,7 +11569,6 @@ keyword_sp: | ONE_SYM {} | PACK_KEYS_SYM {} | PAGE_SYM {} - | PAGE_CHECKSUM_SYM {} | PARTIAL {} | PARTITIONING_SYM {} | PARTITIONS_SYM {} @@ -11625,7 +11645,6 @@ keyword_sp: | TEXT_SYM {} | THAN_SYM {} | TRANSACTION_SYM {} - | TRANSACTIONAL_SYM {} | TRIGGERS_SYM {} | TIMESTAMP {} | TIMESTAMP_ADD {} diff --git a/sql/table.cc b/sql/table.cc index 60a27e136b1..c1d79bdcdd3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -724,8 +724,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!head[32]) // New frm file in 3.23 { share->avg_row_length= uint4korr(head+34); - share->transactional= (ha_choice) (head[39] & 3); - share->page_checksum= (ha_choice) ((head[39] >> 2) & 3); share->row_type= (row_type) head[40]; share->table_charset= get_charset((uint) head[38],MYF(0)); share->null_field_first= 1; @@ -2492,8 +2490,11 @@ File create_frm(THD *thd, const char *name, const char *db, int4store(fileinfo+34,create_info->avg_row_length); fileinfo[38]= (create_info->default_table_charset ? create_info->default_table_charset->number : 0); - fileinfo[39]= (uchar) ((uint) create_info->transactional | - ((uint) create_info->page_checksum << 2)); + /* + In future versions, we will store in fileinfo[39] the values of the + TRANSACTIONAL and PAGE_CHECKSUM clauses of CREATE TABLE. + */ + fileinfo[39]= 0; fileinfo[40]= (uchar) create_info->row_type; /* Next few bytes where for RAID support */ fileinfo[41]= 0; diff --git a/sql/table.h b/sql/table.h index cb53013cd59..98ede52cd99 100644 --- a/sql/table.h +++ b/sql/table.h @@ -361,7 +361,9 @@ typedef struct st_table_share } enum row_type row_type; /* How rows are stored */ enum tmp_table_type tmp_table; + /** Transactional or not. Unused; reserved for future versions. */ enum ha_choice transactional; + /** Per-page checksums or not. Unused; reserved for future versions. */ enum ha_choice page_checksum; uint ref_count; /* How many TABLE objects uses this */ |