From f8ed839cd93545075a53e7a37d2e3dc96702ef9a Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 16 Mar 2005 08:50:21 +0100 Subject: [PATCH 1/7] WinAMD64 compat fix --- client/mysql.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysql.cc b/client/mysql.cc index 16b26cd745d..b2f75e6f1e4 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2154,7 +2154,7 @@ print_table_data_xml(MYSQL_RES *result) for (uint i=0; i < mysql_num_fields(result); i++) { tee_fprintf(PAGER, "\t"); xmlencode_print(cur[i], lengths[i]); tee_fprintf(PAGER, "\n"); From 99e581ecc1ee5537c918ef010ac92a27fa96944d Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 16 Mar 2005 12:45:08 +0100 Subject: [PATCH 2/7] sql/ha_innodb.cc protect prepare-...-commit with a mutex to ensure that commits in binlog and in the innodb have the same order store binlog position with the commit --- innobase/include/trx0trx.h | 5 +++-- sql/ha_innodb.cc | 40 +++++++++++++++++++++++++++----------- sql/handler.cc | 3 --- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 9db69261468..64e9c87fbbd 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -390,8 +390,9 @@ struct trx_struct{ dulint table_id; /* table id if the preceding field is TRUE */ /*------------------------------*/ - int active_trans; /* whether a transaction in MySQL - is active */ + int active_trans; /* 1 - if a transaction in MySQL + is active. 2 - if prepare_commit_mutex + was taken */ void* mysql_thd; /* MySQL thread handle corresponding to this trx, or NULL */ char** mysql_query_str;/* pointer to the field in mysqld_thd diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 73d5ac9e94e..bf4acf65a0c 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -45,7 +45,8 @@ have disables the InnoDB inlining in this file. */ #include "ha_innodb.h" -pthread_mutex_t innobase_mutex; +pthread_mutex_t innobase_share_mutex, // to protect innobase_open_files + prepare_commit_mutex; // to force correct commit order in binlog bool innodb_inited= 0; /* Store MySQL definition of 'byte': in Linux it is char while InnoDB @@ -1266,7 +1267,8 @@ innobase_init(void) (void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0, (hash_get_key) innobase_get_key, 0, 0); - pthread_mutex_init(&innobase_mutex, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST); innodb_inited= 1; /* If this is a replication slave and we needed to do a crash recovery, @@ -1320,7 +1322,8 @@ innobase_end(void) hash_free(&innobase_open_tables); my_free(internal_innobase_data_file_path, MYF(MY_ALLOW_ZERO_PTR)); - pthread_mutex_destroy(&innobase_mutex); + pthread_mutex_destroy(&innobase_share_mutex); + pthread_mutex_destroy(&prepare_commit_mutex); } DBUG_RETURN(err); @@ -1478,9 +1481,20 @@ innobase_commit( /* We were instructed to commit the whole transaction, or this is an SQL statement end and autocommit is on */ + /* We need current binlog position for HotBackup to work. + Note, the position is current because of prepare_commit_mutex */ + trx->mysql_log_file_name = mysql_bin_log.get_log_fname(); + trx->mysql_log_offset = + (ib_longlong)mysql_bin_log.get_log_file()->pos_in_file; + innobase_commit_low(trx); + if (trx->active_trans == 2) { + + pthread_mutex_unlock(&prepare_commit_mutex); + } trx->active_trans = 0; + } else { /* We just mark the SQL statement ended and do not do a transaction commit */ @@ -5842,7 +5856,7 @@ static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length, static INNOBASE_SHARE *get_share(const char *table_name) { INNOBASE_SHARE *share; - pthread_mutex_lock(&innobase_mutex); + pthread_mutex_lock(&innobase_share_mutex); uint length=(uint) strlen(table_name); if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables, (mysql_byte*) table_name, @@ -5856,7 +5870,7 @@ static INNOBASE_SHARE *get_share(const char *table_name) strmov(share->table_name,table_name); if (my_hash_insert(&innobase_open_tables, (mysql_byte*) share)) { - pthread_mutex_unlock(&innobase_mutex); + pthread_mutex_unlock(&innobase_share_mutex); my_free((gptr) share,0); return 0; } @@ -5865,13 +5879,13 @@ static INNOBASE_SHARE *get_share(const char *table_name) } } share->use_count++; - pthread_mutex_unlock(&innobase_mutex); + pthread_mutex_unlock(&innobase_share_mutex); return share; } static void free_share(INNOBASE_SHARE *share) { - pthread_mutex_lock(&innobase_mutex); + pthread_mutex_lock(&innobase_share_mutex); if (!--share->use_count) { hash_delete(&innobase_open_tables, (mysql_byte*) share); @@ -5879,7 +5893,7 @@ static void free_share(INNOBASE_SHARE *share) pthread_mutex_destroy(&share->mutex); my_free((gptr) share, MYF(0)); } - pthread_mutex_unlock(&innobase_mutex); + pthread_mutex_unlock(&innobase_share_mutex); } /********************************************************************* @@ -6318,15 +6332,19 @@ innobase_xa_prepare( FALSE - the current SQL statement ended */ { int error = 0; - trx_t* trx; + trx_t* trx = check_trx_exists(thd); + + if (thd->lex->sql_command != SQLCOM_XA_PREPARE) { + + pthread_mutex_lock(&prepare_commit_mutex); + trx->active_trans = 2; + } if (!thd->variables.innodb_support_xa) { return(0); } - trx = check_trx_exists(thd); - trx->xid=thd->transaction.xid; /* Release a possible FIFO ticket and search latch. Since we will diff --git a/sql/handler.cc b/sql/handler.cc index 67a69201e3d..c7bd65bf24c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -526,7 +526,6 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) /* RETURN - -1 - cannot prepare 0 - ok 1 - error, transaction was rolled back */ @@ -539,8 +538,6 @@ int ha_prepare(THD *thd) #ifdef USING_TRANSACTIONS if (trans->nht) { - if (trans->no_2pc) - DBUG_RETURN(-1); for (; *ht; ht++) { int err; From f3cf0837d26055ce65c8fa83125c0be24831bdfa Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 16 Mar 2005 23:47:38 +0100 Subject: [PATCH 3/7] log_event.cc: #ifdef USING_TRANSACTIONS ha_innodb.cc: comment rpl_drop_temp.test, rpl_drop_temp.result: cleanup --- mysql-test/r/rpl_drop_temp.result | 1 + mysql-test/t/rpl_drop_temp.test | 2 ++ sql/ha_innodb.cc | 21 ++++++++++++++++++++- sql/log_event.cc | 2 ++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/rpl_drop_temp.result b/mysql-test/r/rpl_drop_temp.result index e00309cac8f..04fe094ea26 100644 --- a/mysql-test/r/rpl_drop_temp.result +++ b/mysql-test/r/rpl_drop_temp.result @@ -10,3 +10,4 @@ create temporary table mysqltest.t2 (n int); show status like 'Slave_open_temp_tables'; Variable_name Value Slave_open_temp_tables 0 +drop database mysqltest; diff --git a/mysql-test/t/rpl_drop_temp.test b/mysql-test/t/rpl_drop_temp.test index 73d691d9d90..41cd2edd139 100644 --- a/mysql-test/t/rpl_drop_temp.test +++ b/mysql-test/t/rpl_drop_temp.test @@ -11,3 +11,5 @@ disconnect master; connection slave; --real_sleep 3; # time for DROP to be written show status like 'Slave_open_temp_tables'; +drop database mysqltest; + diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 0ccac274d58..072f6866d7a 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -1483,7 +1483,7 @@ innobase_commit( /* We were instructed to commit the whole transaction, or this is an SQL statement end and autocommit is on */ - /* We need current binlog position for HotBackup to work. + /* We need current binlog position for ibbackup to work. Note, the position is current because of prepare_commit_mutex */ trx->mysql_log_file_name = mysql_bin_log.get_log_fname(); trx->mysql_log_offset = @@ -6472,6 +6472,25 @@ innobase_xa_prepare( if (thd->lex->sql_command != SQLCOM_XA_PREPARE) { + /* For ibbackup to work the order of transactions in binlog + and InnoDB must be the same. Consider the situation + + thread1> prepare; write to binlog; ... + + thread2> prepare; write to binlog; commit + thread1> ... commit + + To ensure this will not happen we're taking the mutex on + prepare, and releasing it on commit. + + Note: only do it for normal commits, done via ha_commit_trans. + If 2pc protocol is executed by external transaction + coordinator, it will be just a regular MySQL client + executing XA PREPARE and XA COMMIT commands. + In this case we cannot know how many minutes or hours + will be between XA PREPARE and XA COMMIT, and we don't want + to block for undefined period of time. + */ pthread_mutex_lock(&prepare_commit_mutex); trx->active_trans = 2; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 3176cdfd5cb..72fc9185d4c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2012,6 +2012,7 @@ int Format_description_log_event::exec_event(struct st_relay_log_info* rli) delete rli->relay_log.description_event_for_exec; rli->relay_log.description_event_for_exec= this; +#ifdef USING_TRANSACTIONS /* As a transaction NEVER spans on 2 or more binlogs: if we have an active transaction at this point, the master died @@ -2033,6 +2034,7 @@ int Format_description_log_event::exec_event(struct st_relay_log_info* rli) "to its binary log."); end_trans(thd, ROLLBACK); } +#endif /* If this event comes from ourselves, there is no cleaning task to perform, we don't call Start_log_event_v3::exec_event() (this was just to update the From 1400af8841a3665d8a3b2cabd9ebabd6cc161d33 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 16 Mar 2005 23:56:37 +0100 Subject: [PATCH 4/7] drop database on the right server :) --- mysql-test/t/rpl_drop_temp.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/rpl_drop_temp.test b/mysql-test/t/rpl_drop_temp.test index 41cd2edd139..cf663367b78 100644 --- a/mysql-test/t/rpl_drop_temp.test +++ b/mysql-test/t/rpl_drop_temp.test @@ -11,5 +11,6 @@ disconnect master; connection slave; --real_sleep 3; # time for DROP to be written show status like 'Slave_open_temp_tables'; +connection default; drop database mysqltest; From 376a4ee03b84f46fa577e1acdf975a4ee72ec5c2 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Thu, 17 Mar 2005 12:27:45 +0100 Subject: [PATCH 5/7] Field::quote_data(): don't call escape_string_for_mysql() unnecesary don't overwrite local buffer escape_string_for_mysql(): take a length of the destination buffer as an argument --- include/my_sys.h | 3 +- libmysql/libmysql.c | 4 +-- mysys/charset.c | 75 +++++++++++++++++++++++++++++---------------- sql/field.cc | 23 ++++++-------- sql/item.cc | 2 +- sql/sql_prepare.cc | 2 +- 6 files changed, 64 insertions(+), 45 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index afd2803b75d..c4385cd5fd2 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -845,7 +845,8 @@ extern char *get_charsets_dir(char *buf); extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2); extern my_bool init_compiled_charsets(myf flags); extern void add_compiled_collation(CHARSET_INFO *cs); -extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, +extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, const char *from, ulong length); extern void thd_increment_bytes_sent(ulong length); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 20a000f1e4d..7e5d9667be2 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1575,14 +1575,14 @@ mysql_hex_string(char *to, const char *from, ulong length) ulong STDCALL mysql_escape_string(char *to,const char *from,ulong length) { - return escape_string_for_mysql(default_charset_info, to, from, length); + return escape_string_for_mysql(default_charset_info, to, 0, from, length); } ulong STDCALL mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, ulong length) { - return escape_string_for_mysql(mysql->charset, to, from, length); + return escape_string_for_mysql(mysql->charset, to, 0, from, length); } diff --git a/mysys/charset.c b/mysys/charset.c index 4b7ad3e59f4..a4a8205a3f9 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -547,10 +547,10 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, DBUG_PRINT("enter",("name: '%s'", cs_name)); (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ - + cs_number= get_charset_number(cs_name, cs_flags); cs= cs_number ? get_internal_charset(cs_number, flags) : NULL; - + if (!cs && (flags & MY_WME)) { char index_file[FN_REFLEN]; @@ -561,21 +561,34 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, DBUG_RETURN(cs); } - -ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, +/* + NOTE + to keep old C API, to_length may be 0 to mean "big enough" + RETURN + the length of the escaped string or ~0 if it did not fit. +*/ +ulong escape_string_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, const char *from, ulong length) { const char *to_start= to; - const char *end; + const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length); + my_bool overflow=0; #ifdef USE_MB my_bool use_mb_flag= use_mb(charset_info); #endif - for (end= from + length; from != end; from++) + for (end= from + length; from < end; from++) { + char escape=0; #ifdef USE_MB int l; if (use_mb_flag && (l= my_ismbchar(charset_info, from, end))) { + if (to + l >= to_end) + { + overflow=1; + break; + } while (l--) *to++= *from++; from--; @@ -593,45 +606,53 @@ ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \) */ if (use_mb_flag && (l= my_mbcharlen(charset_info, *from)) > 1) - { - *to++= '\\'; - *to++= *from; - continue; - } + escape= *from; + else #endif switch (*from) { case 0: /* Must be escaped for 'mysql' */ - *to++= '\\'; - *to++= '0'; + escape= '0'; break; case '\n': /* Must be escaped for logs */ - *to++= '\\'; - *to++= 'n'; + escape= 'n'; break; case '\r': - *to++= '\\'; - *to++= 'r'; + escape= 'r'; break; case '\\': - *to++= '\\'; - *to++= '\\'; + escape= '\\'; break; case '\'': - *to++= '\\'; - *to++= '\''; + escape= '\''; break; case '"': /* Better safe than sorry */ - *to++= '\\'; - *to++= '"'; + escape= '"'; break; case '\032': /* This gives problems on Win32 */ - *to++= '\\'; - *to++= 'Z'; + escape= 'Z'; break; - default: + } + if (escape) + { + if (to + 2 >= to_end) + { + overflow=1; + break; + } + *to++= '\\'; + *to++= escape; + } + else + { + if (to + 1 >= to_end) + { + overflow=1; + break; + } *to++= *from; } } *to= 0; - return (ulong) (to - to_start); + return overflow ? (ulong)~0 : (ulong) (to - to_start); } + diff --git a/sql/field.cc b/sql/field.cc index b6dd00d62a7..ae9b76e2dc4 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -752,25 +752,22 @@ bool Field::quote_data(String *unquoted_string) { char escaped_string[IO_SIZE]; char *unquoted_string_buffer= (char *)(unquoted_string->ptr()); - uint need_quotes; DBUG_ENTER("Field::quote_data"); - // this is the same call that mysql_real_escape_string() calls - escape_string_for_mysql(&my_charset_bin, (char *)escaped_string, - unquoted_string->ptr(), unquoted_string->length()); - - need_quotes= needs_quotes(); - - if (need_quotes == 0) + if (!needs_quotes()) DBUG_RETURN(0); + // this is the same call that mysql_real_escape_string() calls + if (escape_string_for_mysql(&my_charset_bin, (char *)escaped_string, + sizeof(escaped_string), unquoted_string->ptr(), + unquoted_string->length()) == (ulong)~0) + DBUG_RETURN(1); + // reset string, then re-append with quotes and escaped values unquoted_string->length(0); - if (unquoted_string->append('\'')) - DBUG_RETURN(1); - if (unquoted_string->append((char *)escaped_string)) - DBUG_RETURN(1); - if (unquoted_string->append('\'')) + if (unquoted_string->append('\'') || + unquoted_string->append((char *)escaped_string) || + unquoted_string->append('\'')) DBUG_RETURN(1); DBUG_RETURN(0); } diff --git a/sql/item.cc b/sql/item.cc index e6be934e334..3489c54286c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2038,7 +2038,7 @@ const String *Item_param::query_val_str(String* str) const buf= str->c_ptr_quick(); ptr= buf; *ptr++= '\''; - ptr+= escape_string_for_mysql(str_value.charset(), ptr, + ptr+= escape_string_for_mysql(str_value.charset(), ptr, 0, str_value.ptr(), str_value.length()); *ptr++= '\''; str->length(ptr - buf); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 7862717bb18..1cbc52a2a5a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -865,7 +865,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, *ptr++= '\''; ptr+= escape_string_for_mysql(&my_charset_utf8_general_ci, - ptr, entry->name.str, entry->name.length); + ptr, 0, entry->name.str, entry->name.length); *ptr++= '\''; str.length(ptr - buf); From f66967cf1b05f43ef492a9791f16e303d49b0906 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Thu, 17 Mar 2005 16:58:23 +0100 Subject: [PATCH 6/7] sql/ha_federated.cc: update to new prototype sql/item_cmpfunc.cc: typo fixed --- sql/ha_federated.cc | 1 + sql/item_cmpfunc.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index cddd0fd5927..8000aae180d 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -469,6 +469,7 @@ static int check_foreign_data_source(FEDERATED_SHARE *share) */ query.append("SHOW TABLES LIKE '"); escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_base_name, + sizeof(escaped_table_base_name), share->table_base_name, share->table_base_name_length); query.append(escaped_table_base_name); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c0cb0704852..9850b01561e 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1720,7 +1720,7 @@ void Item_func_coalesce::fix_length_and_dec() decimals= 0; break; case ROW_RESULT: - defaullt: + default: DBUG_ASSERT(0); } } From 54fc6fbfe85775ba5165abff3bf1af661cb06c4d Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Fri, 18 Mar 2005 14:59:07 +0100 Subject: [PATCH 7/7] set _XOPEN_SOURCE differently depending on __STDC_VERSION__ [sigh] --- include/my_global.h | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 2601c53bb92..7ec04377864 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -121,19 +121,36 @@ #endif /* - Solaris include file refers to X/Open document + Solaris 9 include file refers to X/Open document System Interfaces and Headers, Issue 5 - saying we should define _XOPEN_SOURCE=500 to get POSIX.1c prototypes + saying we should define _XOPEN_SOURCE=500 to get POSIX.1c prototypes, but apparently other systems (namely FreeBSD) don't agree. - Furthermore X/Open has since 2004 "System Interfaces, Issue 6" - that dictates _XOPEN_SOURCE=600, but Solaris checks for 500. - So, let's define 500 for solaris only. + + On a newer Solaris 10, the above file recognizes also _XOPEN_SOURCE=600. + Furthermore, it tests that if a program requires older standard + (_XOPEN_SOURCE<600 or _POSIX_C_SOURCE<200112L) it cannot be + run on a new compiler (that defines _STDC_C99) and issues an #error. + It's also an #error if a program requires new standard (_XOPEN_SOURCE=600 + or _POSIX_C_SOURCE=200112L) and a compiler does not define _STDC_C99. + + To add more to this mess, Sun Studio C compiler defines _STDC_C99 while + C++ compiler does not! + + So, in a desperate attempt to get correct prototypes for both + C and C++ code, we define either _XOPEN_SOURCE=600 or _XOPEN_SOURCE=500 + depending on the compiler's announced C standard support. + + Cleaner solutions are welcome. */ #ifdef __sun +#if __STDC_VERSION__ - 0 >= 199901L +#define _XOPEN_SOURCE 600 +#else #define _XOPEN_SOURCE 500 #endif +#endif #if defined(THREAD) && !defined(__WIN__) && !defined(OS2) #ifndef _POSIX_PTHREAD_SEMANTICS