From fee4b2c2710e49fb7db286439063ca7d4265b32f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Mar 2005 16:20:55 +0400 Subject: [PATCH 1/4] Fix for bug #8068 "TIMEDIFF with first negative argument gives wrong result" (and similar bug in ADDTIME/SUBTIME). Both Item_func_add_time/Item_func_timediff::val_str() now use calc_time_diff() function which was backported from 5.0 (and which was was fixed to properly handle microseconds part of its second argument). Also now we correctly set sign of result in case when first argument is negative and second is positive. mysql-test/r/func_sapdb.result: Added test for bug #8068 "TIMEDIFF with first negative argument gives wrong result". mysql-test/t/func_sapdb.test: Added test for bug #8068 "TIMEDIFF with first negative argument gives wrong result". sql/item_timefunc.cc: - Backported calc_time_diff() function from 5.0 tree. Changed it to accept time value as its second argument when its first argument is datetime value. Fixed wrong handling of microsecond part of second argument. - Item_func_add_time::val_str()/Item_func_timediff::val_str() Removed similar pieces of code calculating difference between two datetime values (or their sum) in microseconds. Now we use calc_time_diff() function instead. Also now we correctly set sign of result in case when first argument is negative and second is positive. --- mysql-test/r/func_sapdb.result | 3 + mysql-test/t/func_sapdb.test | 1 + sql/item_timefunc.cc | 171 ++++++++++++++++++++------------- 3 files changed, 107 insertions(+), 68 deletions(-) diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index c74d6fc5a4e..68c3baa7bde 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -185,6 +185,7 @@ insert into test values ('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), ('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), ('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), +('2001-01-01 01:01:01', '-01:01:01', '1 01:01:01', '2001-01-01 01:01:01'), ('2001-01-01 01:01:01', null, '-1 01:01:01', null), (null, null, null, null), ('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); @@ -194,6 +195,7 @@ ttt qqq 2001-01-01 00:00:00 -25:01:00 1997-12-31 00:00:00 -25:01:00 2001-01-01 02:02:02 -24:00:00 +2001-01-01 00:00:00 24:00:00 NULL NULL NULL NULL 2001-01-01 02:02:02 26:02:02 @@ -203,6 +205,7 @@ ttt qqq 26305:01:02 22:58:58 -26305:01:02 -22:58:58 NULL 26:02:02 +00:00:00 -26:02:02 NULL NULL NULL NULL 00:00:00 -24:00:00 diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test index de433485fca..da3affaa36d 100644 --- a/mysql-test/t/func_sapdb.test +++ b/mysql-test/t/func_sapdb.test @@ -100,6 +100,7 @@ insert into test values ('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), ('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), ('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), +('2001-01-01 01:01:01', '-01:01:01', '1 01:01:01', '2001-01-01 01:01:01'), ('2001-01-01 01:01:01', null, '-1 01:01:01', null), (null, null, null, null), ('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2c500f16bf3..6715930bc61 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -761,6 +761,81 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, } +/* + Calculate difference between two datetime values as seconds + microseconds. + + SYNOPSIS + calc_time_diff() + l_time1 - TIME/DATE/DATETIME value + l_time2 - TIME/DATE/DATETIME value + l_sign - 1 absolute values are substracted, + -1 absolute values are added. + seconds_out - Out parameter where difference between + l_time1 and l_time2 in seconds is stored. + microseconds_out- Out parameter where microsecond part of difference + between l_time1 and l_time2 is stored. + + NOTE + This function calculates difference between l_time1 and l_time2 absolute + values. So one should set l_sign and correct result if he want to take + signs into account (i.e. for TIME values). + + RETURN VALUES + Returns sign of difference. + 1 means negative result + 0 means positive result + +*/ + +static bool calc_time_diff(TIME *l_time1, TIME *l_time2, int l_sign, + longlong *seconds_out, long *microseconds_out) +{ + long days; + bool neg; + longlong microseconds; + + /* + We suppose that if first argument is MYSQL_TIMESTAMP_TIME + the second argument should be TIMESTAMP_TIME also. + We should check it before calc_time_diff call. + */ + if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value + days= l_time1->day - l_sign*l_time2->day; + else + { + days= calc_daynr((uint) l_time1->year, + (uint) l_time1->month, + (uint) l_time1->day); + if (l_time2->time_type == MYSQL_TIMESTAMP_TIME) + days-= l_sign*l_time2->day; + else + days-= l_sign*calc_daynr((uint) l_time2->year, + (uint) l_time2->month, + (uint) l_time2->day); + } + + microseconds= ((longlong)days*LL(86400) + + (longlong)(l_time1->hour*3600L + + l_time1->minute*60L + + l_time1->second) - + l_sign*(longlong)(l_time2->hour*3600L + + l_time2->minute*60L + + l_time2->second)) * LL(1000000) + + (longlong)l_time1->second_part - + l_sign*(longlong)l_time2->second_part; + + neg= 0; + if (microseconds < 0) + { + microseconds= -microseconds; + neg= 1; + } + *seconds_out= microseconds/1000000L; + *microseconds_out= (long) (microseconds%1000000L); + return neg; +} + + longlong Item_func_period_add::val_int() { DBUG_ASSERT(fixed == 1); @@ -2332,11 +2407,11 @@ String *Item_func_add_time::val_str(String *str) DBUG_ASSERT(fixed == 1); TIME l_time1, l_time2, l_time3; bool is_time= 0; - long microseconds, seconds, days= 0; + long days, microseconds; + longlong seconds; int l_sign= sign; null_value=0; - l_time3.neg= 0; if (is_date) // TIMESTAMP function { if (get_arg0_date(&l_time1,1) || @@ -2352,51 +2427,26 @@ String *Item_func_add_time::val_str(String *str) l_time2.time_type == MYSQL_TIMESTAMP_DATETIME) goto null_date; is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME); - if (is_time && (l_time2.neg == l_time1.neg && l_time1.neg)) - l_time3.neg= 1; } if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - microseconds= l_time1.second_part + l_sign*l_time2.second_part; - seconds= (l_time1.hour*3600L + l_time1.minute*60L + l_time1.second + - (l_time2.day*86400L + l_time2.hour*3600L + - l_time2.minute*60L + l_time2.second)*l_sign); - if (is_time) - seconds+= l_time1.day*86400L; - else - days+= calc_daynr((uint) l_time1.year,(uint) l_time1.month, - (uint) l_time1.day); - seconds= seconds + microseconds/1000000L; - microseconds= microseconds%1000000L; - days+= seconds/86400L; - seconds= seconds%86400L; + l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign, + &seconds, µseconds); - if (microseconds < 0) - { - microseconds+= 1000000L; - seconds--; - } - if (seconds < 0) - { - days+= seconds/86400L - 1; - seconds+= 86400L; - } - if (days < 0) - { - if (!is_time) - goto null_date; - if (microseconds) - { - microseconds= 1000000L - microseconds; - seconds++; - } - seconds= 86400L - seconds; - days= -(++days); - l_time3.neg= 1; - } + /* + If first argument was negative and diff between arguments + is non-zero we need to swap sign to get proper result. + */ + if (l_time1.neg && (seconds || microseconds)) + l_time3.neg= 1-l_time3.neg; // Swap sign of result - calc_time_from_sec(&l_time3, seconds, microseconds); + if (!is_time && l_time3.neg) + goto null_date; + + days= (long)(seconds/86400L); + + calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds); if (!is_time) { get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day); @@ -2452,8 +2502,8 @@ void Item_func_add_time::print(String *str) String *Item_func_timediff::val_str(String *str) { DBUG_ASSERT(fixed == 1); - longlong microseconds; - long days; + longlong seconds; + long microseconds; int l_sign= 1; TIME l_time1 ,l_time2, l_time3; @@ -2466,33 +2516,18 @@ String *Item_func_timediff::val_str(String *str) if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - if (l_time1.time_type == MYSQL_TIMESTAMP_TIME) // Time value - days= l_time1.day - l_sign*l_time2.day; - else // DateTime value - days= (calc_daynr((uint) l_time1.year, - (uint) l_time1.month, - (uint) l_time1.day) - - l_sign*calc_daynr((uint) l_time2.year, - (uint) l_time2.month, - (uint) l_time2.day)); + l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign, + &seconds, µseconds); - microseconds= ((longlong)days*86400L + - l_time1.hour*3600L + l_time1.minute*60L + l_time1.second - - (longlong)l_sign*(l_time2.hour*3600L + l_time2.minute*60L + - l_time2.second))*1000000 + - l_time1.second_part - l_sign*l_time2.second_part; + /* + For MYSQL_TIMESTAMP_TIME only: + If first argument was negative and diff between arguments + is non-zero we need to swap sign to get proper result. + */ + if (l_time1.neg && (seconds || microseconds)) + l_time3.neg= 1-l_time3.neg; // Swap sign of result - l_time3.neg= 0; - if (microseconds < 0) - { - microseconds= -microseconds; - l_time3.neg= 1; - } - if ((l_time2.neg == l_time1.neg) && l_time1.neg && microseconds) - l_time3.neg= l_time3.neg ? 0 : 1; - - calc_time_from_sec(&l_time3, (long)(microseconds/1000000), - (long)(microseconds%1000000)); + calc_time_from_sec(&l_time3, (long) seconds, microseconds); if (!make_datetime(l_time1.second_part || l_time2.second_part ? TIME_MICROSECOND : TIME_ONLY, From 56ea770272bf6c81873922aa8a0c146f4e9921b0 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Apr 2005 12:17:32 +0300 Subject: [PATCH 2/4] Fixed a bug: deadlock without any locking, simple select and update (Bug #7975). Backported from 5.0.3. innobase/row/row0ins.c: If the SQL-query will update or replace duplicate records we take X-lock for duplicate records. sql/ha_innodb.cc: INSERT ON DUPLICATE KEY UPDATE will also update duplicate records and we should take X-lock in this case for duplicate records. --- innobase/row/row0ins.c | 41 +++++++++++++++++++++++------------------ sql/ha_innodb.cc | 19 +++++++++++++++---- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 15ffabf70cc..5ca1ee51cbd 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -51,14 +51,19 @@ innobase_invalidate_query_cache( chars count */ /********************************************************************** -This function returns true if SQL-query in the current thread +This function returns true if + +1) SQL-query in the current thread is either REPLACE or LOAD DATA INFILE REPLACE. + +2) SQL-query in the current thread +is INSERT ON DUPLICATE KEY UPDATE. + NOTE that /mysql/innobase/row/row0ins.c must contain the prototype for this function ! */ ibool -innobase_query_is_replace(void); -/*===========================*/ +innobase_query_is_update(void); /************************************************************************* Creates an insert node struct. */ @@ -1562,12 +1567,12 @@ row_ins_scan_sec_index_for_duplicate( trx = thr_get_trx(thr); ut_ad(trx); - if (innobase_query_is_replace()) { + if (innobase_query_is_update()) { - /* The manual defines the REPLACE semantics that it - is either an INSERT or DELETE(s) for duplicate key - + INSERT. Therefore, we should take X-lock for - duplicates */ + /* If the SQL-query will update or replace + duplicate key we will take X-lock for + duplicates ( REPLACE, LOAD DATAFILE REPLACE, + INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_ORDINARY,rec,index,thr); @@ -1675,12 +1680,12 @@ row_ins_duplicate_error_in_clust( sure that in roll-forward we get the same duplicate errors as in original execution */ - if (innobase_query_is_replace()) { + if (innobase_query_is_update()) { - /* The manual defines the REPLACE semantics - that it is either an INSERT or DELETE(s) - for duplicate key + INSERT. Therefore, we - should take X-lock for duplicates */ + /* If the SQL-query will update or replace + duplicate key we will take X-lock for + duplicates ( REPLACE, LOAD DATAFILE REPLACE, + INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_REC_NOT_GAP,rec,cursor->index, @@ -1713,12 +1718,12 @@ row_ins_duplicate_error_in_clust( if (rec != page_get_supremum_rec(page)) { - /* The manual defines the REPLACE semantics that it - is either an INSERT or DELETE(s) for duplicate key - + INSERT. Therefore, we should take X-lock for - duplicates. */ + if (innobase_query_is_update()) { - if (innobase_query_is_replace()) { + /* If the SQL-query will update or replace + duplicate key we will take X-lock for + duplicates ( REPLACE, LOAD DATAFILE REPLACE, + INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_REC_NOT_GAP, diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 6cb35fb392d..06d9bf24c13 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5653,13 +5653,19 @@ innobase_get_at_most_n_mbchars( extern "C" { /********************************************************************** -This function returns true if SQL-query in the current thread +This function returns true if + +1) SQL-query in the current thread is either REPLACE or LOAD DATA INFILE REPLACE. + +2) SQL-query in the current thread +is INSERT ON DUPLICATE KEY UPDATE. + NOTE that /mysql/innobase/row/row0ins.c must contain the prototype for this function ! */ ibool -innobase_query_is_replace(void) +innobase_query_is_update(void) /*===========================*/ { THD* thd; @@ -5671,9 +5677,14 @@ innobase_query_is_replace(void) ( thd->lex->sql_command == SQLCOM_LOAD && thd->lex->duplicates == DUP_REPLACE )) { return true; - } else { - return false; } + + if ( thd->lex->sql_command == SQLCOM_INSERT && + thd->lex->duplicates == DUP_UPDATE ) { + return true; + } + + return false; } } From 992d2305914b79f07c062ac0c35b95804105b40b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Apr 2005 08:23:03 +0300 Subject: [PATCH 3/4] Style change. Use 1 and 0 instead of true and false. --- sql/ha_innodb.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 06d9bf24c13..83c72594dfb 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5666,7 +5666,7 @@ prototype for this function ! */ ibool innobase_query_is_update(void) -/*===========================*/ +/*==========================*/ { THD* thd; @@ -5676,15 +5676,15 @@ innobase_query_is_update(void) thd->lex->sql_command == SQLCOM_REPLACE_SELECT || ( thd->lex->sql_command == SQLCOM_LOAD && thd->lex->duplicates == DUP_REPLACE )) { - return true; + return(1); } if ( thd->lex->sql_command == SQLCOM_INSERT && thd->lex->duplicates == DUP_UPDATE ) { - return true; + return(1); } - return false; + return(0); } } From 92a00143d7460c964523494e573508c151b9cba4 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Apr 2005 11:17:32 +0200 Subject: [PATCH 4/4] CSC5149 - ndb test programs Fix src distributions for benchmark prg --- ndb/test/ndbapi/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndb/test/ndbapi/Makefile.am b/ndb/test/ndbapi/Makefile.am index 0c84db8c068..6f04ac3fce2 100644 --- a/ndb/test/ndbapi/Makefile.am +++ b/ndb/test/ndbapi/Makefile.am @@ -70,8 +70,8 @@ test_event_SOURCES = test_event.cpp ndbapi_slow_select_SOURCES = slow_select.cpp testReadPerf_SOURCES = testReadPerf.cpp testLcp_SOURCES = testLcp.cpp -DbCreate_SOURCES= bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp -DbAsyncGenerator_SOURCES= bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp +DbCreate_SOURCES= bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp bench/dbPopulate.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp +DbAsyncGenerator_SOURCES= bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp bench/dbGenerator.h bench/macros.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel