From 1401c2c71c5a99a6ac8e340138c5569fabd3f42e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Jul 2006 13:28:30 +0400 Subject: [PATCH 1/3] Better comments for void Item::top_level_item() --- sql/item.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/item.h b/sql/item.h index eca8ee184a1..3eab695cb5e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -275,9 +275,16 @@ public: Any new item which can be NULL must implement this call. */ virtual bool is_null() { return 0; } + /* - it is "top level" item of WHERE clause and we do not need correct NULL - handling + Inform the item that there will be no distinction between its result + being FALSE or NULL. + + NOTE + This function will be called for eg. Items that are top-level AND-parts + of the WHERE clause. Items implementing this function (currently + Item_cond_and and subquery-related item) enable special optimizations + when they are "top level". */ virtual void top_level_item() {} /* From cf7627bce09928d66129280b7903f6ab42fe2983 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Jul 2006 11:11:49 -0700 Subject: [PATCH 2/3] Fixed bug #18243. The implementation of the method Item_func_reverse::val_str for the REVERSE function modified the argument of the function. This led to wrong results for expressions that contained REVERSE(ref) if ref occurred somewhere else in the expressions. mysql-test/r/func_str.result: Added a test case for bug #18243. mysql-test/t/func_str.test: Added a test case for bug #18243. sql/item_strfunc.cc: Fixed bug #18243. The implementation of the method Item_func_reverse::val_str for the REVERSE function modified the argument of the function. This led to wrong results for expressions that contained REVERSE(ref) if ref occurred somewhere else in the expressions. The implementation of Item_func_reverse::val_str has been changed to make the argument intact. sql/item_strfunc.h: Fixed bug #18243. Added tmp_value to the Item_func_reverse class to store the result of the function. It erroneously replaced the argument before this fix. --- mysql-test/r/func_str.result | 15 ++++++++++++++ mysql-test/t/func_str.test | 17 ++++++++++++++++ sql/item_strfunc.cc | 39 +++++++++++++++++++----------------- sql/item_strfunc.h | 1 + 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 24e6bb6f38a..61a77c211d7 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1021,4 +1021,19 @@ select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); f1 f2 test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 drop table t1; +CREATE TABLE t1 (a varchar(10)); +INSERT INTO t1 VALUES ('abc'), ('xyz'); +SELECT a, CONCAT(a,' ',a) AS c FROM t1 +HAVING LEFT(c,LENGTH(c)-INSTR(REVERSE(c)," ")) = a; +a c +abc abc abc +xyz xyz xyz +SELECT a, CONCAT(a,' ',a) AS c FROM t1 +HAVING LEFT(CONCAT(a,' ',a), +LENGTH(CONCAT(a,' ',a))- +INSTR(REVERSE(CONCAT(a,' ',a))," ")) = a; +a c +abc abc abc +xyz xyz xyz +DROP TABLE t1; End of 4.1 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index c36e15a08b9..9a1c75a8dc0 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -681,4 +681,21 @@ select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); drop table t1; +# +# Bug#18243: REVERSE changes its argument +# + +CREATE TABLE t1 (a varchar(10)); +INSERT INTO t1 VALUES ('abc'), ('xyz'); + +SELECT a, CONCAT(a,' ',a) AS c FROM t1 + HAVING LEFT(c,LENGTH(c)-INSTR(REVERSE(c)," ")) = a; + +SELECT a, CONCAT(a,' ',a) AS c FROM t1 + HAVING LEFT(CONCAT(a,' ',a), + LENGTH(CONCAT(a,' ',a))- + INSTR(REVERSE(CONCAT(a,' ',a))," ")) = a; + +DROP TABLE t1; + --echo End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ee585649f8c..7bc7956283b 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -709,44 +709,47 @@ String *Item_func_reverse::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); - char *ptr,*end; + char *ptr, *end, *tmp; if ((null_value=args[0]->null_value)) return 0; /* An empty string is a special case as the string pointer may be null */ if (!res->length()) return &my_empty_string; - res=copy_if_not_alloced(str,res,res->length()); - ptr = (char *) res->ptr(); - end=ptr+res->length(); + if (tmp_value.alloced_length() < res->length() && + tmp_value.realloc(res->length())) + { + null_value= 1; + return 0; + } + tmp_value.length(res->length()); + tmp_value.set_charset(res->charset()); + ptr= (char *) res->ptr(); + end= ptr + res->length(); + tmp= (char *) tmp_value.ptr() + tmp_value.length(); #ifdef USE_MB if (use_mb(res->charset())) { - String tmpstr; - tmpstr.copy(*res); - char *tmp = (char *) tmpstr.ptr() + tmpstr.length(); register uint32 l; while (ptr < end) { - if ((l=my_ismbchar(res->charset(), ptr,end))) - tmp-=l, memcpy(tmp,ptr,l), ptr+=l; + if ((l= my_ismbchar(res->charset(),ptr,end))) + { + tmp-= l; + memcpy(tmp,ptr,l); + ptr+= l; + } else - *--tmp=*ptr++; + *--tmp= *ptr++; } - memcpy((char *) res->ptr(),(char *) tmpstr.ptr(), res->length()); } else #endif /* USE_MB */ { - char tmp; while (ptr < end) - { - tmp=*ptr; - *ptr++=*--end; - *end=tmp; - } + *--tmp= *ptr++; } - return res; + return &tmp_value; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 89bab4a909c..f800c17182b 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -100,6 +100,7 @@ public: class Item_func_reverse :public Item_str_func { + String tmp_value; public: Item_func_reverse(Item *a) :Item_str_func(a) {} String *val_str(String *); From 0806d9a86d73a1f1e089c9c3465c77ca82829b40 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jul 2006 16:27:03 +0300 Subject: [PATCH 3/3] BUG#14553: NULL in WHERE resets LAST_INSERT_ID To make MySQL compatible with some ODBC applications, you can find the AUTO_INCREMENT value for the last inserted row with the following query: SELECT * FROM tbl_name WHERE auto_col IS NULL. This is done with a special code that replaces 'auto_col IS NULL' with 'auto_col = LAST_INSERT_ID'. However this also resets the LAST_INSERT_ID to 0 as it uses it for a flag so as to ensure that only the first SELECT ... WHERE auto_col IS NULL after an INSERT has this special behaviour. In order to avoid resetting the LAST_INSERT_ID a special flag is introduced in the THD class. This flag is used to restrict the second and subsequent SELECTs instead of LAST_INSERT_ID. mysql-test/r/odbc.result: test suite for the bug mysql-test/r/rpl_insert_id.result: test for the fix in replication mysql-test/t/odbc.test: test suite for the bug mysql-test/t/rpl_insert_id.test: test for the fix in replication sql/sql_class.cc: initialize the flag sql/sql_class.h: flag's declaration and set code when setting the last_insert_id sql/sql_select.cc: the special flag is used instead of last_insert_id --- mysql-test/r/odbc.result | 11 +++++++++++ mysql-test/r/rpl_insert_id.result | 14 ++++++++++++++ mysql-test/t/odbc.test | 10 ++++++++++ mysql-test/t/rpl_insert_id.test | 20 +++++++++++++++++++- sql/sql_class.cc | 1 + sql/sql_class.h | 3 +++ sql/sql_select.cc | 4 ++-- 7 files changed, 60 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/odbc.result b/mysql-test/r/odbc.result index c0b2ada0053..30d839077f3 100644 --- a/mysql-test/r/odbc.result +++ b/mysql-test/r/odbc.result @@ -14,3 +14,14 @@ explain select * from t1 where b is null; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables drop table t1; +CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t1 VALUES (NULL); +SELECT sql_no_cache a, last_insert_id() FROM t1 WHERE a IS NULL; +a last_insert_id() +1 1 +SELECT sql_no_cache a, last_insert_id() FROM t1 WHERE a IS NULL; +a last_insert_id() +SELECT sql_no_cache a, last_insert_id() FROM t1; +a last_insert_id() +1 1 +DROP TABLE t1; diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index 8482f631553..e683ea2fb39 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -73,3 +73,17 @@ CREATE TABLE t1 ( a INT UNIQUE ); SET FOREIGN_KEY_CHECKS=0; INSERT INTO t1 VALUES (1),(1); ERROR 23000: Duplicate entry '1' for key 1 +drop table t1; +create table t1(a int auto_increment, key(a)); +create table t2(a int); +insert into t1 (a) values (null); +insert into t2 (a) select a from t1 where a is null; +insert into t2 (a) select a from t1 where a is null; +select * from t2; +a +1 +select * from t2; +a +1 +drop table t1; +drop table t2; diff --git a/mysql-test/t/odbc.test b/mysql-test/t/odbc.test index d4b6fc35e74..6a754bb32a7 100644 --- a/mysql-test/t/odbc.test +++ b/mysql-test/t/odbc.test @@ -21,4 +21,14 @@ select * from t1 where a is null; explain select * from t1 where b is null; drop table t1; +# +# Bug #14553: NULL in WHERE resets LAST_INSERT_ID +# +CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY); +INSERT INTO t1 VALUES (NULL); +SELECT sql_no_cache a, last_insert_id() FROM t1 WHERE a IS NULL; +SELECT sql_no_cache a, last_insert_id() FROM t1 WHERE a IS NULL; +SELECT sql_no_cache a, last_insert_id() FROM t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 49d3a03640c..766afad9e47 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -73,5 +73,23 @@ SET FOREIGN_KEY_CHECKS=0; --error 1062 INSERT INTO t1 VALUES (1),(1); sync_slave_with_master; - + +# +# Bug#14553: NULL in WHERE resets LAST_INSERT_ID +# +connection master; +drop table t1; +create table t1(a int auto_increment, key(a)); +create table t2(a int); +insert into t1 (a) values (null); +insert into t2 (a) select a from t1 where a is null; +insert into t2 (a) select a from t1 where a is null; +select * from t2; +sync_slave_with_master; +connection slave; +select * from t2; +connection master; +drop table t1; +drop table t2; +sync_slave_with_master; # End of 4.1 tests diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d278ebe8dfa..0e51edd985b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -265,6 +265,7 @@ THD::THD() ulong tmp=sql_rnd_with_mutex(); randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); } + substitute_null_with_insert_id = FALSE; } diff --git a/sql/sql_class.h b/sql/sql_class.h index d482a524934..006136a92f1 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -893,6 +893,8 @@ public: bool last_cuted_field; bool no_errors, password, is_fatal_error; bool query_start_used,last_insert_id_used,insert_id_used,rand_used; + /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ + bool substitute_null_with_insert_id; bool time_zone_used; bool in_lock_tables; bool query_error, bootstrap, cleanup_done; @@ -988,6 +990,7 @@ public: { last_insert_id= id_arg; insert_id_used=1; + substitute_null_with_insert_id= TRUE; } inline ulonglong insert_id(void) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 57fb9738612..b6bd9c8b41b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4719,7 +4719,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id()) + thd->insert_id() && thd->substitute_null_with_insert_id) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -4733,7 +4733,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) cond=new_cond; cond->fix_fields(thd, 0, &cond); } - thd->insert_id(0); // Clear for next request + thd->substitute_null_with_insert_id= FALSE; // Clear for next request } /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */ else if (((field->type() == FIELD_TYPE_DATE) ||