1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

WL#3146 "less locking in auto_increment":

this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements. 
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully 
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.

Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().

Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
This commit is contained in:
guilhem@gbichot3.local
2006-07-09 17:52:19 +02:00
parent a3deb94518
commit 0594e1b84b
30 changed files with 1225 additions and 343 deletions

View File

@ -14947,7 +14947,193 @@ static void test_bug14169()
rc= mysql_query(mysql, "drop table t1");
myquery(rc);
}/*
}
/*
Test that mysql_insert_id() behaves as documented in our manual
*/
static void test_mysql_insert_id()
{
my_ulonglong res;
int rc;
myheader("test_mysql_insert_id");
rc= mysql_query(mysql, "drop table if exists t1");
myquery(rc);
/* table without auto_increment column */
rc= mysql_query(mysql, "create table t1 (f1 int, f2 varchar(255), key(f1))");
myquery(rc);
rc= mysql_query(mysql, "insert into t1 values (1,'a')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t1 values (null,'b')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t1 select 5,'c'");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t1 select null,'d'");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t1 values (null,last_insert_id(300))");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 300);
rc= mysql_query(mysql, "insert into t1 select null,last_insert_id(400)");
myquery(rc);
res= mysql_insert_id(mysql);
/*
Behaviour change: old code used to return 0; but 400 is consistent
with INSERT VALUES, and the manual's section of mysql_insert_id() does not
say INSERT SELECT should be different.
*/
DIE_UNLESS(res == 400);
/* table with auto_increment column */
rc= mysql_query(mysql, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))");
myquery(rc);
rc= mysql_query(mysql, "insert into t2 values (1,'a')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 1);
/* this should not influence next INSERT if it doesn't have auto_inc */
rc= mysql_query(mysql, "insert into t1 values (10,'e')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t2 values (null,'b')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 2);
rc= mysql_query(mysql, "insert into t2 select 5,'c'");
myquery(rc);
res= mysql_insert_id(mysql);
/*
Manual says that for multirow insert this should have been 5, but does not
say for INSERT SELECT. This is a behaviour change: old code used to return
0. We try to be consistent with INSERT VALUES.
*/
DIE_UNLESS(res == 5);
rc= mysql_query(mysql, "insert into t2 select null,'d'");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 6);
/* with more than one row */
rc= mysql_query(mysql, "insert into t2 values (10,'a'),(11,'b')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 11);
rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'");
myquery(rc);
res= mysql_insert_id(mysql);
/*
Manual says that for multirow insert this should have been 13, but does
not say for INSERT SELECT. This is a behaviour change: old code used to
return 0. We try to be consistent with INSERT VALUES.
*/
DIE_UNLESS(res == 13);
rc= mysql_query(mysql, "insert into t2 values (null,'a'),(null,'b')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 14);
rc= mysql_query(mysql, "insert into t2 select null,'a' union select null,'b'");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 16);
rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'");
myquery_r(rc);
rc= mysql_query(mysql, "insert ignore into t2 select 12,'a' union select 13,'b'");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert into t2 values (12,'a'),(13,'b')");
myquery_r(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "insert ignore into t2 values (12,'a'),(13,'b')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
/* mixing autogenerated and explicit values */
rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b')");
myquery_r(rc);
rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b'),(25,'g')");
myquery_r(rc);
rc= mysql_query(mysql, "insert into t2 values (null,last_insert_id(300))");
myquery(rc);
res= mysql_insert_id(mysql);
/*
according to the manual, this might be 20 or 300, but it looks like
auto_increment column takes priority over last_insert_id().
*/
DIE_UNLESS(res == 20);
/* If first autogenerated number fails and 2nd works: */
rc= mysql_query(mysql, "drop table t2");
myquery(rc);
rc= mysql_query(mysql, "create table t2 (f1 int not null primary key "
"auto_increment, f2 varchar(255), unique (f2))");
myquery(rc);
rc= mysql_query(mysql, "insert into t2 values (null,'e')");
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 1);
rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(null,'a'),(null,'e')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 2);
/* If autogenerated fails and explicit works: */
rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(12,'c'),(null,'d')");
myquery(rc);
res= mysql_insert_id(mysql);
/*
Behaviour change: old code returned 3 (first autogenerated, even if it
fails); we now return first successful autogenerated.
*/
DIE_UNLESS(res == 13);
/* UPDATE may update mysql_insert_id() if it uses LAST_INSERT_ID(#) */
rc= mysql_query(mysql, "update t2 set f1=14 where f1=12");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "update t2 set f1=NULL where f1=14");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
rc= mysql_query(mysql, "update t2 set f2=last_insert_id(372) where f1=0");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 372);
/* check that LAST_INSERT_ID() does not update mysql_insert_id(): */
rc= mysql_query(mysql, "insert into t2 values (null,'g')");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 15);
rc= mysql_query(mysql, "update t2 set f2=(@li:=last_insert_id()) where f1=15");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 0);
/*
Behaviour change: now if ON DUPLICATE KEY UPDATE updates a row,
mysql_insert_id() returns the id of the row, instead of not being
affected.
*/
rc= mysql_query(mysql, "insert into t2 values (null,@li) on duplicate key "
"update f2=concat('we updated ',f2)");
myquery(rc);
res= mysql_insert_id(mysql);
DIE_UNLESS(res == 15);
rc= mysql_query(mysql, "drop table t1,t2");
myquery(rc);
}
/*
Read and parse arguments and MySQL options from my.cnf
*/
@ -15214,6 +15400,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug15613", test_bug15613 },
{ "test_bug14169", test_bug14169 },
{ "test_bug17667", test_bug17667 },
{ "test_mysql_insert_id", test_mysql_insert_id },
{ 0, 0 }
};