mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -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;
|
|||||||
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)),
|
|||||||
(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
|
|||||||
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
|
|||||||
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;
|
|||||||
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 |
|
|||||||
|
|
||||||
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)),
|
|||||||
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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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)
|
|||||||
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)
|
|||||||
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;
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user