diff options
-rw-r--r-- | innobase/include/row0mysql.h | 2 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 11 | ||||
-rw-r--r-- | mysql-test/include/innodb_rollback_on_timeout.inc | 37 | ||||
-rwxr-xr-x | mysql-test/mysql-test-run.pl | 6 | ||||
-rw-r--r-- | mysql-test/r/func_str.result | 12 | ||||
-rw-r--r-- | mysql-test/r/innodb_mysql.result | 36 | ||||
-rw-r--r-- | mysql-test/r/innodb_timeout_rollback.result | 35 | ||||
-rw-r--r-- | mysql-test/t/func_str.test | 8 | ||||
-rw-r--r-- | mysql-test/t/innodb_mysql-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/innodb_mysql.test | 2 | ||||
-rw-r--r-- | mysql-test/t/innodb_timeout_rollback-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/innodb_timeout_rollback.test | 5 | ||||
-rw-r--r-- | scripts/make_binary_distribution.sh | 1 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 7 | ||||
-rw-r--r-- | sql/ha_innodb.h | 1 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 39 | ||||
-rw-r--r-- | sql/mysqld.cc | 7 | ||||
-rw-r--r-- | sql/set_var.cc | 1 | ||||
-rw-r--r-- | strings/decimal.c | 34 |
19 files changed, 216 insertions, 30 deletions
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index 7d8740db044..9e28fabe491 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -19,6 +19,8 @@ Created 9/17/2000 Heikki Tuuri #include "btr0pcur.h" #include "trx0types.h" +extern ibool row_rollback_on_timeout; + typedef struct row_prebuilt_struct row_prebuilt_t; /*********************************************************************** diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index efbb93ba9f5..4bc5f39359c 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -35,6 +35,9 @@ Created 9/17/2000 Heikki Tuuri /* A dummy variable used to fool the compiler */ ibool row_mysql_identically_false = FALSE; +/* Provide optional 4.x backwards compatibility for 5.0 and above */ +ibool row_rollback_on_timeout = FALSE; + /* List of tables we should drop in background. ALTER TABLE in MySQL requires that the table handler can drop the table in background when there are no queries to it any more. Protected by the kernel mutex. */ @@ -514,7 +517,9 @@ handle_new_error: return(TRUE); } else if (err == DB_DEADLOCK - || err == DB_LOCK_TABLE_FULL) { + || err == DB_LOCK_TABLE_FULL + || (err == DB_LOCK_WAIT_TIMEOUT + && row_rollback_on_timeout)) { /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ @@ -522,6 +527,10 @@ handle_new_error: } else if (err == DB_OUT_OF_FILE_SPACE || err == DB_LOCK_WAIT_TIMEOUT) { + + ut_ad(!(err == DB_LOCK_WAIT_TIMEOUT + && row_rollback_on_timeout)); + if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ diff --git a/mysql-test/include/innodb_rollback_on_timeout.inc b/mysql-test/include/innodb_rollback_on_timeout.inc new file mode 100644 index 00000000000..73c7374c79e --- /dev/null +++ b/mysql-test/include/innodb_rollback_on_timeout.inc @@ -0,0 +1,37 @@ +# +# Bug #24200: Provide backwards compatibility mode for 4.x "rollback on +# transaction timeout" +# +show variables like 'innodb_rollback_on_timeout'; +create table t1 (a int unsigned not null primary key) engine = innodb; +insert into t1 values (1); +commit; +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +connection con2; +begin work; +insert into t1 values (2); +select * from t1; + +connection con1; +begin work; +insert into t1 values (5); +select * from t1; +# Lock wait timeout set to 2 seconds in <THIS TEST>-master.opt; this +# statement will time out; in 5.0.13+, it will not roll back transaction. +--error ER_LOCK_WAIT_TIMEOUT +insert into t1 values (2); +# On 5.0.13+, this should give ==> 1, 5 +select * from t1; +commit; + +connection con2; +select * from t1; +commit; + +connection default; +select * from t1; +drop table t1; +disconnect con1; +disconnect con2; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c2d8f8ae2f8..36f5b0a776f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -58,6 +58,7 @@ $Devel::Trace::TRACE= 0; # Don't trace boring init stuff use File::Path; use File::Basename; use File::Copy; +use File::Temp qw / tempdir /; use Cwd; use Getopt::Long; use Sys::Hostname; @@ -1020,6 +1021,11 @@ sub command_line_setup () { my $sockdir = $opt_tmpdir; $sockdir =~ s|/+$||; + # On some operating systems, there is a limit to the length of a + # UNIX domain socket's path far below PATH_MAX, so try to avoid long + # socket path names. + $sockdir = tempdir(CLEANUP => 1) if ( length($sockdir) > 80 ); + # Put this into a hash, will be a C struct $master->[0]= diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 14c61a2dd93..b5a9d4de105 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1916,4 +1916,16 @@ CHAR(0xff,0x8f USING utf8) IS NULL Warnings: Error 1300 Invalid utf8 character string: 'FF8F' SET SQL_MODE=@orig_sql_mode; +select substring('abc', cast(2 as unsigned int)); +substring('abc', cast(2 as unsigned int)) +bc +select repeat('a', cast(2 as unsigned int)); +repeat('a', cast(2 as unsigned int)) +aa +select rpad('abc', cast(5 as unsigned integer), 'x'); +rpad('abc', cast(5 as unsigned integer), 'x') +abcxx +select lpad('abc', cast(5 as unsigned integer), 'x'); +lpad('abc', cast(5 as unsigned integer), 'x') +xxabc End of 5.0 tests diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 012f9492d8d..f150af4b6c2 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -383,4 +383,40 @@ EXPLAIN SELECT SQL_BIG_RESULT b, SUM(c) FROM t1 GROUP BY b; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 128 Using filesort DROP TABLE t1; +show variables like 'innodb_rollback_on_timeout'; +Variable_name Value +innodb_rollback_on_timeout OFF +create table t1 (a int unsigned not null primary key) engine = innodb; +insert into t1 values (1); +commit; +begin work; +insert into t1 values (2); +select * from t1; +a +1 +2 +begin work; +insert into t1 values (5); +select * from t1; +a +1 +5 +insert into t1 values (2); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +select * from t1; +a +1 +5 +commit; +select * from t1; +a +1 +2 +commit; +select * from t1; +a +1 +2 +5 +drop table t1; End of 5.0 tests diff --git a/mysql-test/r/innodb_timeout_rollback.result b/mysql-test/r/innodb_timeout_rollback.result new file mode 100644 index 00000000000..b25a2bbd815 --- /dev/null +++ b/mysql-test/r/innodb_timeout_rollback.result @@ -0,0 +1,35 @@ +show variables like 'innodb_rollback_on_timeout'; +Variable_name Value +innodb_rollback_on_timeout ON +create table t1 (a int unsigned not null primary key) engine = innodb; +insert into t1 values (1); +commit; +begin work; +insert into t1 values (2); +select * from t1; +a +1 +2 +begin work; +insert into t1 values (5); +select * from t1; +a +1 +5 +insert into t1 values (2); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +select * from t1; +a +1 +commit; +select * from t1; +a +1 +2 +commit; +select * from t1; +a +1 +2 +drop table t1; +End of 5.0 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index af4f8b9a9d2..a55b633b91e 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -991,5 +991,13 @@ SELECT CHAR(0xff,0x8f USING utf8) IS NULL; SET SQL_MODE=@orig_sql_mode; +# +# Bug #24947: problem with some string function with unsigned int parameters +# + +select substring('abc', cast(2 as unsigned int)); +select repeat('a', cast(2 as unsigned int)); +select rpad('abc', cast(5 as unsigned integer), 'x'); +select lpad('abc', cast(5 as unsigned integer), 'x'); --echo End of 5.0 tests diff --git a/mysql-test/t/innodb_mysql-master.opt b/mysql-test/t/innodb_mysql-master.opt new file mode 100644 index 00000000000..205c733455d --- /dev/null +++ b/mysql-test/t/innodb_mysql-master.opt @@ -0,0 +1 @@ +--innodb-lock-wait-timeout=2 diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 06cfe71ef11..45a2ede091b 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -384,4 +384,6 @@ EXPLAIN SELECT SQL_BIG_RESULT b, SUM(c) FROM t1 GROUP BY b; DROP TABLE t1; +--source include/innodb_rollback_on_timeout.inc + --echo End of 5.0 tests diff --git a/mysql-test/t/innodb_timeout_rollback-master.opt b/mysql-test/t/innodb_timeout_rollback-master.opt new file mode 100644 index 00000000000..50921bb4df0 --- /dev/null +++ b/mysql-test/t/innodb_timeout_rollback-master.opt @@ -0,0 +1 @@ +--innodb_lock_wait_timeout=2 --innodb_rollback_on_timeout diff --git a/mysql-test/t/innodb_timeout_rollback.test b/mysql-test/t/innodb_timeout_rollback.test new file mode 100644 index 00000000000..99890971064 --- /dev/null +++ b/mysql-test/t/innodb_timeout_rollback.test @@ -0,0 +1,5 @@ +-- source include/have_innodb.inc + +--source include/innodb_rollback_on_timeout.inc + +--echo End of 5.0 tests diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index a6a2f1e3b49..9469cca875d 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -243,6 +243,7 @@ $CP mysql-test/include/*.inc $BASE/mysql-test/include $CP mysql-test/include/*.test $BASE/mysql-test/include $CP mysql-test/t/*.def $BASE/mysql-test/t $CP mysql-test/std_data/*.dat mysql-test/std_data/*.frm \ + mysql-test/std_data/*.MYD mysql-test/std_data/*.MYI \ mysql-test/std_data/*.pem mysql-test/std_data/Moscow_leap \ mysql-test/std_data/des_key_file mysql-test/std_data/*.*001 \ mysql-test/std_data/*.cnf \ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 9f367313fb0..456fd27d9d9 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -173,6 +173,7 @@ my_bool innobase_use_large_pages = FALSE; my_bool innobase_use_native_aio = FALSE; my_bool innobase_file_per_table = FALSE; my_bool innobase_locks_unsafe_for_binlog = FALSE; +my_bool innobase_rollback_on_timeout = FALSE; my_bool innobase_create_status_file = FALSE; static char *internal_innobase_data_file_path = NULL; @@ -467,6 +468,10 @@ convert_error_code_to_mysql( latest SQL statement in a lock wait timeout. Previously, we rolled back the whole transaction. */ + if (thd && row_rollback_on_timeout) { + ha_rollback(thd); + } + return(HA_ERR_LOCK_WAIT_TIMEOUT); } else if (error == (int) DB_NO_REFERENCED_ROW) { @@ -1380,6 +1385,8 @@ innobase_init(void) os_use_large_pages = (ibool) innobase_use_large_pages; os_large_page_size = (ulint) innobase_large_page_size; + row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout; + srv_file_per_table = (ibool) innobase_file_per_table; srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog; diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 95d7a9437ad..60bc0e9c7cf 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -216,6 +216,7 @@ extern my_bool innobase_log_archive, innobase_use_large_pages, innobase_use_native_aio, innobase_file_per_table, innobase_locks_unsafe_for_binlog, + innobase_rollback_on_timeout, innobase_create_status_file; extern my_bool innobase_very_fast_shutdown; /* set this to 1 just before calling innobase_end() if you want diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 0b48a00012a..afc2edb06dd 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1166,7 +1166,8 @@ String *Item_func_substr::val_str(String *str) /* if "unsigned_flag" is set, we have a *huge* positive number. */ /* Assumes that the maximum length of a String is < INT_MAX32. */ - if ((args[1]->unsigned_flag) || (start < INT_MIN32) || (start > INT_MAX32)) + if ((!args[1]->unsigned_flag && (start < INT_MIN32 || start > INT_MAX32)) || + (args[1]->unsigned_flag && ((ulonglong) start > INT_MAX32))) return &my_empty_string; start= ((start < 0) ? res->numchars() + start : start - 1); @@ -2227,25 +2228,23 @@ String *Item_func_repeat::val_str(String *str) uint length,tot_length; char *to; /* must be longlong to avoid truncation */ - longlong tmp_count= args[1]->val_int(); - long count= (long) tmp_count; + longlong count= args[1]->val_int(); String *res= args[0]->val_str(str); - /* Assumes that the maximum length of a String is < INT_MAX32. */ - /* Bounds check on count: If this is triggered, we will error. */ - if ((tmp_count > INT_MAX32) || args[1]->unsigned_flag) - count= INT_MAX32; - if (args[0]->null_value || args[1]->null_value) goto err; // string and/or delim are null null_value= 0; - if ((tmp_count <= 0) && !args[1]->unsigned_flag) // For nicer SQL code + if ((count <= 0) && !args[1]->unsigned_flag) // For nicer SQL code return &my_empty_string; + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Bounds check on count: If this is triggered, we will error. */ + if ((ulonglong) count > INT_MAX32) + count= INT_MAX32; if (count == 1) // To avoid reallocs return res; length=res->length(); // Safe length check - if (length > current_thd->variables.max_allowed_packet/count) + if (length > current_thd->variables.max_allowed_packet / (uint) count) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -2319,15 +2318,14 @@ String *Item_func_rpad::val_str(String *str) String *res= args[0]->val_str(str); String *rpad= args[2]->val_str(&rpad_str); + if (!res || args[1]->null_value || !rpad || + ((count < 0) && !args[1]->unsigned_flag)) + goto err; + null_value=0; /* Assumes that the maximum length of a String is < INT_MAX32. */ /* Set here so that rest of code sees out-of-bound value as such. */ - if ((count > INT_MAX32) || args[1]->unsigned_flag) + if ((ulonglong) count > INT_MAX32) count= INT_MAX32; - - if (!res || args[1]->null_value || !rpad || count < 0) - goto err; - null_value=0; - if (count <= (res_char_length= res->numchars())) { // String to pad is big enough res->length(res->charpos((int) count)); // Shorten result if longer @@ -2421,14 +2419,15 @@ String *Item_func_lpad::val_str(String *str) String *res= args[0]->val_str(&tmp_value); String *pad= args[2]->val_str(&lpad_str); + if (!res || args[1]->null_value || !pad || + ((count < 0) && !args[1]->unsigned_flag)) + goto err; + null_value=0; /* Assumes that the maximum length of a String is < INT_MAX32. */ /* Set here so that rest of code sees out-of-bound value as such. */ - if ((count > INT_MAX32) || args[1]->unsigned_flag) + if ((ulonglong) count > INT_MAX32) count= INT_MAX32; - if (!res || args[1]->null_value || !pad || count < 0) - goto err; - null_value=0; res_char_length= res->numchars(); if (count <= res_char_length) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9411ba2ef12..5890a2af21d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4692,7 +4692,8 @@ enum options_mysqld OPT_LOG_SLOW_ADMIN_STATEMENTS, OPT_TABLE_LOCK_WAIT_TIMEOUT, OPT_PORT_OPEN_TIMEOUT, - OPT_MERGE + OPT_MERGE, + OPT_INNODB_ROLLBACK_ON_TIMEOUT }; @@ -4968,6 +4969,10 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, (gptr*) &srv_max_purge_lag, (gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1L, 0}, + {"innodb_rollback_on_timeout", OPT_INNODB_ROLLBACK_ON_TIMEOUT, + "Roll back the complete transaction on lock wait timeout, for 4.x compatibility (disabled by default)", + (gptr*) &innobase_rollback_on_timeout, (gptr*) &innobase_rollback_on_timeout, + 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"innodb_status_file", OPT_INNODB_STATUS_FILE, "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file", (gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file, diff --git a/sql/set_var.cc b/sql/set_var.cc index 23afef742a6..5bbab6224ef 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -904,6 +904,7 @@ struct show_var_st init_vars[]= { {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG }, + {"innodb_rollback_on_timeout", (char*) &innobase_rollback_on_timeout, SHOW_MY_BOOL}, {sys_innodb_support_xa.name, (char*) &sys_innodb_support_xa, SHOW_SYS}, {sys_innodb_sync_spin_loops.name, (char*) &sys_innodb_sync_spin_loops, SHOW_SYS}, {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, diff --git a/strings/decimal.c b/strings/decimal.c index 6b7a2266194..bdc3b1eef42 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -138,6 +138,12 @@ static const dec1 frac_max[DIG_PER_DEC1-1]={ 900000000, 990000000, 999000000, 999900000, 999990000, 999999000, 999999900, 999999990 }; +static double scaler10[]= { + 1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90 +}; +static double scaler1[]= { + 1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 +}; #ifdef HAVE_purify #define sanity(d) DBUG_ASSERT((d)->len > 0) @@ -946,15 +952,27 @@ fatal_error: int decimal2double(decimal_t *from, double *to) { - double x=0, t=DIG_BASE; - int intg, frac; - dec1 *buf=from->buf; + double result= 0.0; + int i, exp= 0; + dec1 *buf= from->buf; + + for (i= from->intg; i > 0; i-= DIG_PER_DEC1) + result= result * DIG_BASE + *buf++; + + for (i= from->frac; i > 0; i-= DIG_PER_DEC1) { + result= result * DIG_BASE + *buf++; + exp+= DIG_PER_DEC1; + } + + DBUG_PRINT("info", ("interm.: %f %d %f", result, exp, + scaler10[exp / 10] * scaler1[exp % 10])); + + result/= scaler10[exp / 10] * scaler1[exp % 10]; + + *to= from->sign ? -result : result; + + DBUG_PRINT("info", ("result: %f (%lx)", *to, *to)); - for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1) - x=x*DIG_BASE + *buf++; - for (frac=from->frac; frac > 0; frac-=DIG_PER_DEC1, t*=DIG_BASE) - x+=*buf++/t; - *to=from->sign ? -x : x; return E_DEC_OK; } |