Loading sql/sql_class.h +2 −1 Original line number Diff line number Diff line Loading @@ -2048,7 +2048,8 @@ class select_insert :public select_result_interceptor { TABLE_LIST *table_list; TABLE *table; List<Item> *fields; ulonglong last_insert_id; ulonglong autoinc_value_of_last_inserted_row; // not autogenerated ulonglong autoinc_value_of_first_inserted_row; // autogenerated COPY_INFO info; bool insert_into_view; select_insert(TABLE_LIST *table_list_par, Loading sql/sql_insert.cc +40 −8 Original line number Diff line number Diff line Loading @@ -2644,7 +2644,8 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, enum_duplicates duplic, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), autoinc_value_of_last_inserted_row(0), autoinc_value_of_first_inserted_row(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); Loading Loading @@ -2897,15 +2898,25 @@ bool select_insert::send_data(List<Item> &values) } if (table->next_number_field) { /* If no value has been autogenerated so far, we need to remember the value we just saw, we may need to send it to client in the end. */ if (!thd->insert_id_used) autoinc_value_of_last_inserted_row= table->next_number_field->val_int(); /* Clear auto-increment field for the next record, if triggers are used we will clear it twice, but this should be cheap. */ table->next_number_field->reset(); if (!last_insert_id && thd->insert_id_used) last_insert_id= thd->last_insert_id; if (!autoinc_value_of_last_inserted_row && thd->insert_id_used) autoinc_value_of_last_inserted_row= thd->last_insert_id; } } if (thd->insert_id_used && !autoinc_value_of_first_inserted_row) autoinc_value_of_first_inserted_row= thd->last_insert_id; DBUG_RETURN(error); } Loading Loading @@ -2934,6 +2945,7 @@ bool select_insert::send_eof() { int error, error2; bool changed, transactional_table= table->file->has_transactions(); ulonglong id; DBUG_ENTER("select_insert::send_eof"); error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; Loading @@ -2954,8 +2966,17 @@ bool select_insert::send_eof() DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table); if (last_insert_id) thd->insert_id(info.copied ? last_insert_id : 0); // For binary log // For binary log if (autoinc_value_of_last_inserted_row) { if (info.copied) thd->insert_id(autoinc_value_of_last_inserted_row); else { autoinc_value_of_first_inserted_row= 0; thd->insert_id(0); } } /* Write to binlog before commiting transaction */ if (mysql_bin_log.is_open()) { Loading @@ -2982,7 +3003,9 @@ bool select_insert::send_eof() thd->row_count_func= info.copied + info.deleted + ((thd->client_capabilities & CLIENT_FOUND_ROWS) ? info.touched : info.updated); ::send_ok(thd, (ulong) thd->row_count_func, last_insert_id, buff); id= autoinc_value_of_first_inserted_row > 0 ? autoinc_value_of_first_inserted_row : thd->last_insert_id; ::send_ok(thd, (ulong) thd->row_count_func, id, buff); DBUG_RETURN(0); } Loading Loading @@ -3011,8 +3034,17 @@ void select_insert::abort() if ((changed= info.copied || info.deleted || info.updated) && !transactional_table) { if (last_insert_id) thd->insert_id(last_insert_id); // For binary log // For binary log if (autoinc_value_of_last_inserted_row) { if (info.copied) thd->insert_id(autoinc_value_of_last_inserted_row); else { autoinc_value_of_first_inserted_row= 0; thd->insert_id(0); } } if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query, thd->query_length, Loading tests/mysql_client_test.c +182 −0 Original line number Diff line number Diff line Loading @@ -15199,6 +15199,187 @@ static void test_bug14169() } /* 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); DIE_UNLESS(res == 3); /* 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); } /* Bug#20152: mysql_stmt_execute() writes to MYSQL_TYPE_DATE buffer */ Loading Loading @@ -16237,6 +16418,7 @@ static struct my_tests_st my_tests[]= { { "test_bug17667", test_bug17667 }, { "test_bug19671", test_bug19671 }, { "test_bug15752", test_bug15752 }, { "test_mysql_insert_id", test_mysql_insert_id }, { "test_bug21206", test_bug21206 }, { "test_bug21726", test_bug21726 }, { "test_bug15518", test_bug15518 }, Loading Loading
sql/sql_class.h +2 −1 Original line number Diff line number Diff line Loading @@ -2048,7 +2048,8 @@ class select_insert :public select_result_interceptor { TABLE_LIST *table_list; TABLE *table; List<Item> *fields; ulonglong last_insert_id; ulonglong autoinc_value_of_last_inserted_row; // not autogenerated ulonglong autoinc_value_of_first_inserted_row; // autogenerated COPY_INFO info; bool insert_into_view; select_insert(TABLE_LIST *table_list_par, Loading
sql/sql_insert.cc +40 −8 Original line number Diff line number Diff line Loading @@ -2644,7 +2644,8 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, enum_duplicates duplic, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), autoinc_value_of_last_inserted_row(0), autoinc_value_of_first_inserted_row(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); Loading Loading @@ -2897,15 +2898,25 @@ bool select_insert::send_data(List<Item> &values) } if (table->next_number_field) { /* If no value has been autogenerated so far, we need to remember the value we just saw, we may need to send it to client in the end. */ if (!thd->insert_id_used) autoinc_value_of_last_inserted_row= table->next_number_field->val_int(); /* Clear auto-increment field for the next record, if triggers are used we will clear it twice, but this should be cheap. */ table->next_number_field->reset(); if (!last_insert_id && thd->insert_id_used) last_insert_id= thd->last_insert_id; if (!autoinc_value_of_last_inserted_row && thd->insert_id_used) autoinc_value_of_last_inserted_row= thd->last_insert_id; } } if (thd->insert_id_used && !autoinc_value_of_first_inserted_row) autoinc_value_of_first_inserted_row= thd->last_insert_id; DBUG_RETURN(error); } Loading Loading @@ -2934,6 +2945,7 @@ bool select_insert::send_eof() { int error, error2; bool changed, transactional_table= table->file->has_transactions(); ulonglong id; DBUG_ENTER("select_insert::send_eof"); error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; Loading @@ -2954,8 +2966,17 @@ bool select_insert::send_eof() DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table); if (last_insert_id) thd->insert_id(info.copied ? last_insert_id : 0); // For binary log // For binary log if (autoinc_value_of_last_inserted_row) { if (info.copied) thd->insert_id(autoinc_value_of_last_inserted_row); else { autoinc_value_of_first_inserted_row= 0; thd->insert_id(0); } } /* Write to binlog before commiting transaction */ if (mysql_bin_log.is_open()) { Loading @@ -2982,7 +3003,9 @@ bool select_insert::send_eof() thd->row_count_func= info.copied + info.deleted + ((thd->client_capabilities & CLIENT_FOUND_ROWS) ? info.touched : info.updated); ::send_ok(thd, (ulong) thd->row_count_func, last_insert_id, buff); id= autoinc_value_of_first_inserted_row > 0 ? autoinc_value_of_first_inserted_row : thd->last_insert_id; ::send_ok(thd, (ulong) thd->row_count_func, id, buff); DBUG_RETURN(0); } Loading Loading @@ -3011,8 +3034,17 @@ void select_insert::abort() if ((changed= info.copied || info.deleted || info.updated) && !transactional_table) { if (last_insert_id) thd->insert_id(last_insert_id); // For binary log // For binary log if (autoinc_value_of_last_inserted_row) { if (info.copied) thd->insert_id(autoinc_value_of_last_inserted_row); else { autoinc_value_of_first_inserted_row= 0; thd->insert_id(0); } } if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query, thd->query_length, Loading
tests/mysql_client_test.c +182 −0 Original line number Diff line number Diff line Loading @@ -15199,6 +15199,187 @@ static void test_bug14169() } /* 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); DIE_UNLESS(res == 3); /* 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); } /* Bug#20152: mysql_stmt_execute() writes to MYSQL_TYPE_DATE buffer */ Loading Loading @@ -16237,6 +16418,7 @@ static struct my_tests_st my_tests[]= { { "test_bug17667", test_bug17667 }, { "test_bug19671", test_bug19671 }, { "test_bug15752", test_bug15752 }, { "test_mysql_insert_id", test_mysql_insert_id }, { "test_bug21206", test_bug21206 }, { "test_bug21726", test_bug21726 }, { "test_bug15518", test_bug15518 }, Loading