Commit 37b5cbdc authored by unknown's avatar unknown

Fix for the patch for bug#21726: Incorrect result with multiple

invocations of LAST_INSERT_ID.

Reding of LAST_INSERT_ID inside stored function wasn't noted by caller,
and no LAST_INSERT_ID_EVENT was issued for binary log.

The solution is to add THD::last_insert_id_used_bin_log, which is much
like THD::last_insert_id_used, but is reset only for upper-level
statements.  This new variable is used to issue LAST_INSERT_ID_EVENT.


mysql-test/r/rpl_insert_id.result:
  For bug#21726, add result for statement-based replication of function
  calls.
mysql-test/t/rpl_insert_id.test:
  For bug#21726, add test case for statement-based replication of function
  calls.
sql/item_func.cc:
  Set THD::last_insert_id_used_bin_log for issuing of LAST_INSERT_ID_EVENT.
sql/log.cc:
  Issue LAST_INSERT_ID_EVENT if THD::last_insert_id_used_bin_log is set.
sql/set_var.cc:
  Set THD::last_insert_id_used_bin_log for issuing of LAST_INSERT_ID_EVENT.
sql/sql_class.cc:
  Initialize THD::last_insert_id_used_bin_log.
  Fix typo, add whitespace.
sql/sql_class.h:
  Add THD::last_insert_id_used_bin_log.
sql/sql_parse.cc:
  Reset THD::last_insert_id_used_bin_log for upper-level statements.
sql/sql_select.cc:
  Set THD::last_insert_id_used_bin_log for issuing of LAST_INSERT_ID_EVENT.
parent be929087
...@@ -272,6 +272,7 @@ DROP TABLE t1, t2; ...@@ -272,6 +272,7 @@ DROP TABLE t1, t2;
DROP PROCEDURE IF EXISTS p1; DROP PROCEDURE IF EXISTS p1;
DROP FUNCTION IF EXISTS f1; DROP FUNCTION IF EXISTS f1;
DROP FUNCTION IF EXISTS f2; DROP FUNCTION IF EXISTS f2;
DROP FUNCTION IF EXISTS f3;
DROP TABLE IF EXISTS t1, t2; DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 ( CREATE TABLE t1 (
i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, i INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
...@@ -295,6 +296,11 @@ RETURN 0; ...@@ -295,6 +296,11 @@ RETURN 0;
END | END |
CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC
RETURN LAST_INSERT_ID() | RETURN LAST_INSERT_ID() |
CREATE FUNCTION f3() RETURNS INT MODIFIES SQL DATA
BEGIN
INSERT INTO t2 (i) VALUES (LAST_INSERT_ID());
RETURN 0;
END |
INSERT INTO t1 VALUES (NULL, -1); INSERT INTO t1 VALUES (NULL, -1);
CALL p1(); CALL p1();
SELECT f1(); SELECT f1();
...@@ -307,6 +313,11 @@ INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), ...@@ -307,6 +313,11 @@ INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)),
(NULL, @@LAST_INSERT_ID); (NULL, @@LAST_INSERT_ID);
INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID());
UPDATE t1 SET j= -1 WHERE i IS NULL; UPDATE t1 SET j= -1 WHERE i IS NULL;
INSERT INTO t1 (i) VALUES (NULL);
INSERT INTO t1 (i) VALUES (NULL);
SELECT f3();
f3()
0
SELECT * FROM t1; SELECT * FROM t1;
i j i j
1 -1 1 -1
...@@ -327,12 +338,15 @@ i j ...@@ -327,12 +338,15 @@ i j
16 13 16 13
17 -1 17 -1
18 14 18 14
19 0
20 0
SELECT * FROM t2; SELECT * FROM t2;
i i
2 2
3 3
5 5
6 6
19
SELECT * FROM t1; SELECT * FROM t1;
i j i j
1 -1 1 -1
...@@ -353,15 +367,19 @@ i j ...@@ -353,15 +367,19 @@ i j
16 13 16 13
17 -1 17 -1
18 14 18 14
19 0
20 0
SELECT * FROM t2; SELECT * FROM t2;
i i
2 2
3 3
5 5
6 6
19
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP FUNCTION f1; DROP FUNCTION f1;
DROP FUNCTION f2; DROP FUNCTION f2;
DROP FUNCTION f3;
DROP TABLE t1, t2; DROP TABLE t1, t2;
# End of 5.0 tests # End of 5.0 tests
......
...@@ -299,6 +299,7 @@ DROP TABLE t1, t2; ...@@ -299,6 +299,7 @@ DROP TABLE t1, t2;
DROP PROCEDURE IF EXISTS p1; DROP PROCEDURE IF EXISTS p1;
DROP FUNCTION IF EXISTS f1; DROP FUNCTION IF EXISTS f1;
DROP FUNCTION IF EXISTS f2; DROP FUNCTION IF EXISTS f2;
DROP FUNCTION IF EXISTS f3;
DROP TABLE IF EXISTS t1, t2; DROP TABLE IF EXISTS t1, t2;
--enable_warnings --enable_warnings
...@@ -328,6 +329,12 @@ END | ...@@ -328,6 +329,12 @@ END |
CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC
RETURN LAST_INSERT_ID() | RETURN LAST_INSERT_ID() |
CREATE FUNCTION f3() RETURNS INT MODIFIES SQL DATA
BEGIN
INSERT INTO t2 (i) VALUES (LAST_INSERT_ID());
RETURN 0;
END |
delimiter ;| delimiter ;|
INSERT INTO t1 VALUES (NULL, -1); INSERT INTO t1 VALUES (NULL, -1);
...@@ -342,6 +349,15 @@ INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), ...@@ -342,6 +349,15 @@ INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)),
INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID());
UPDATE t1 SET j= -1 WHERE i IS NULL; UPDATE t1 SET j= -1 WHERE i IS NULL;
# Test statement-based replication of function calls.
INSERT INTO t1 (i) VALUES (NULL);
connection master1;
INSERT INTO t1 (i) VALUES (NULL);
connection master;
SELECT f3();
SELECT * FROM t1; SELECT * FROM t1;
SELECT * FROM t2; SELECT * FROM t2;
...@@ -353,6 +369,7 @@ connection master; ...@@ -353,6 +369,7 @@ connection master;
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP FUNCTION f1; DROP FUNCTION f1;
DROP FUNCTION f2; DROP FUNCTION f2;
DROP FUNCTION f3;
DROP TABLE t1, t2; DROP TABLE t1, t2;
......
...@@ -3362,6 +3362,7 @@ bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref) ...@@ -3362,6 +3362,7 @@ bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref)
id of the previous statement in THD::current_insert_id. id of the previous statement in THD::current_insert_id.
*/ */
thd->last_insert_id_used= TRUE; thd->last_insert_id_used= TRUE;
thd->last_insert_id_used_bin_log= TRUE;
thd->current_insert_id= thd->last_insert_id; thd->current_insert_id= thd->last_insert_id;
} }
null_value= FALSE; null_value= FALSE;
......
...@@ -1702,7 +1702,7 @@ bool MYSQL_LOG::write(Log_event *event_info) ...@@ -1702,7 +1702,7 @@ bool MYSQL_LOG::write(Log_event *event_info)
if (thd) if (thd)
{ {
if (thd->last_insert_id_used) if (thd->last_insert_id_used_bin_log)
{ {
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
thd->current_insert_id); thd->current_insert_id);
...@@ -1994,7 +1994,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, ...@@ -1994,7 +1994,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
tmp_errno=errno; tmp_errno=errno;
strmov(db,thd->db); strmov(db,thd->db);
} }
if (thd->last_insert_id_used) if (thd->last_insert_id_used_bin_log)
{ {
end=strmov(end,",last_insert_id="); end=strmov(end,",last_insert_id=");
end=longlong10_to_str((longlong) thd->current_insert_id,end,-10); end=longlong10_to_str((longlong) thd->current_insert_id,end,-10);
......
...@@ -2579,6 +2579,7 @@ byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type, ...@@ -2579,6 +2579,7 @@ byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
of the previous statement in THD::current_insert_id. of the previous statement in THD::current_insert_id.
*/ */
thd->last_insert_id_used= TRUE; thd->last_insert_id_used= TRUE;
thd->last_insert_id_used_bin_log= TRUE;
thd->current_insert_id= thd->last_insert_id; thd->current_insert_id= thd->last_insert_id;
} }
return (byte*) &thd->current_insert_id; return (byte*) &thd->current_insert_id;
......
...@@ -179,9 +179,9 @@ THD::THD() ...@@ -179,9 +179,9 @@ THD::THD()
lock_id(&main_lock_id), lock_id(&main_lock_id),
user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0), user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
rand_used(0), time_zone_used(0), rand_used(0), time_zone_used(0),
last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0),
in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE), clear_next_insert_id(0), in_lock_tables(0), bootstrap(0),
spcont(NULL) derived_tables_processing(FALSE), spcont(NULL)
{ {
stmt_arena= this; stmt_arena= this;
thread_stack= 0; thread_stack= 0;
...@@ -560,7 +560,7 @@ bool THD::store_globals() ...@@ -560,7 +560,7 @@ bool THD::store_globals()
THD::cleanup_after_query() THD::cleanup_after_query()
DESCRIPTION DESCRIPTION
This function is used to reset thread data to it's default state. This function is used to reset thread data to its default state.
NOTE NOTE
This function is not suitable for setting thread data to some This function is not suitable for setting thread data to some
...@@ -568,6 +568,7 @@ bool THD::store_globals() ...@@ -568,6 +568,7 @@ bool THD::store_globals()
different master threads may overwrite data of each other on different master threads may overwrite data of each other on
slave. slave.
*/ */
void THD::cleanup_after_query() void THD::cleanup_after_query()
{ {
last_insert_id_used= FALSE; last_insert_id_used= FALSE;
...@@ -582,6 +583,7 @@ void THD::cleanup_after_query() ...@@ -582,6 +583,7 @@ void THD::cleanup_after_query()
where= THD::DEFAULT_WHERE; where= THD::DEFAULT_WHERE;
} }
/* /*
Convert a string to another character set Convert a string to another character set
......
...@@ -1340,11 +1340,20 @@ public: ...@@ -1340,11 +1340,20 @@ public:
/* /*
last_insert_id_used is set when current statement calls last_insert_id_used is set when current statement calls
LAST_INSERT_ID() or reads @@LAST_INSERT_ID, so that binary log LAST_INSERT_ID() or reads @@LAST_INSERT_ID.
LAST_INSERT_ID_EVENT be generated.
*/ */
bool last_insert_id_used; bool last_insert_id_used;
/*
last_insert_id_used is set when current statement or any stored
function called from this statement calls LAST_INSERT_ID() or
reads @@LAST_INSERT_ID, so that binary log LAST_INSERT_ID_EVENT be
generated. Required for statement-based binary log for issuing
"SET LAST_INSERT_ID= #" before "SELECT func()", if func() reads
LAST_INSERT_ID.
*/
bool last_insert_id_used_bin_log;
/* /*
insert_id_used is set when current statement updates insert_id_used is set when current statement updates
THD::last_insert_id, so that binary log INSERT_ID_EVENT be THD::last_insert_id, so that binary log INSERT_ID_EVENT be
......
...@@ -5651,6 +5651,7 @@ void mysql_reset_thd_for_next_command(THD *thd) ...@@ -5651,6 +5651,7 @@ void mysql_reset_thd_for_next_command(THD *thd)
thd->free_list= 0; thd->free_list= 0;
thd->select_number= 1; thd->select_number= 1;
thd->query_start_used= thd->insert_id_used=0; thd->query_start_used= thd->insert_id_used=0;
thd->last_insert_id_used_bin_log= FALSE;
thd->is_fatal_error= thd->time_zone_used= 0; thd->is_fatal_error= thd->time_zone_used= 0;
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_INDEX_USED |
......
...@@ -8154,11 +8154,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) ...@@ -8154,11 +8154,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
21)))) 21))))
{ {
/* /*
Set THD::last_insert_id_used manually, as this statement Set THD::last_insert_id_used_bin_log manually, as this
uses LAST_INSERT_ID() in a sense, and should issue statement uses LAST_INSERT_ID() in a sense, and should
LAST_INSERT_ID_EVENT. issue LAST_INSERT_ID_EVENT.
*/ */
thd->last_insert_id_used= TRUE; thd->last_insert_id_used_bin_log= TRUE;
cond=new_cond; cond=new_cond;
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment