Commit 837ba8cc authored by unknown's avatar unknown

Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID

Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables
Merge from 5.0.
Changed auto_increment handling to the 5.1 pattern.


mysql-test/r/delayed.result:
  Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID
  Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables
  Merge from 5.0.
  Updated the test result.
parent ef9dcc18
...@@ -144,7 +144,7 @@ INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); ...@@ -144,7 +144,7 @@ INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73);
INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83);
SET insert_id= 114; SET insert_id= 114;
INSERT INTO t1 VALUES(NULL, 91); INSERT INTO t1 VALUES(NULL, 91);
ERROR 23000: Duplicate entry '114' for key 1 ERROR 23000: Duplicate entry '114' for key 'PRIMARY'
INSERT INTO t1 VALUES (NULL, 92), (NULL, 93); INSERT INTO t1 VALUES (NULL, 92), (NULL, 93);
SELECT * FROM t1; SELECT * FROM t1;
c1 c2 c1 c2
......
...@@ -572,7 +572,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -572,7 +572,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
joins_freed= TRUE; joins_freed= TRUE;
table->file->ha_release_auto_increment();
/* /*
Now all rows are inserted. Time to update logs and sends response to Now all rows are inserted. Time to update logs and sends response to
...@@ -591,6 +590,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -591,6 +590,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
else else
#endif #endif
{ {
/*
Do not do this release if this is a delayed insert, it would steal
auto_inc values from the delayed_insert thread as they share TABLE.
*/
table->file->ha_release_auto_increment();
if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error) if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{ {
table->file->print_error(my_errno,MYF(0)); table->file->print_error(my_errno,MYF(0));
...@@ -1330,7 +1334,7 @@ class delayed_row :public ilink { ...@@ -1330,7 +1334,7 @@ class delayed_row :public ilink {
bool query_start_used, ignore, log_query; bool query_start_used, ignore, log_query;
bool stmt_depends_on_first_successful_insert_id_in_prev_stmt; bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
ulonglong first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_prev_stmt;
ulonglong next_insert_id; ulonglong forced_insert_id;
ulong auto_increment_increment; ulong auto_increment_increment;
ulong auto_increment_offset; ulong auto_increment_offset;
timestamp_auto_set_type timestamp_field_type; timestamp_auto_set_type timestamp_field_type;
...@@ -1339,7 +1343,7 @@ class delayed_row :public ilink { ...@@ -1339,7 +1343,7 @@ class delayed_row :public ilink {
delayed_row(LEX_STRING const query_arg, enum_duplicates dup_arg, delayed_row(LEX_STRING const query_arg, enum_duplicates dup_arg,
bool ignore_arg, bool log_query_arg) bool ignore_arg, bool log_query_arg)
: record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg), : record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg),
query(query_arg) forced_insert_id(0), query(query_arg)
{} {}
~delayed_row() ~delayed_row()
{ {
...@@ -1697,6 +1701,7 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic, ...@@ -1697,6 +1701,7 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic,
{ {
delayed_row *row; delayed_row *row;
delayed_insert *di=thd->di; delayed_insert *di=thd->di;
const Discrete_interval *forced_auto_inc;
DBUG_ENTER("write_delayed"); DBUG_ENTER("write_delayed");
DBUG_PRINT("enter", ("query = '%s' length %u", query.str, query.length)); DBUG_PRINT("enter", ("query = '%s' length %u", query.str, query.length));
...@@ -1746,21 +1751,16 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic, ...@@ -1746,21 +1751,16 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic,
thd->first_successful_insert_id_in_prev_stmt; thd->first_successful_insert_id_in_prev_stmt;
row->timestamp_field_type= table->timestamp_field_type; row->timestamp_field_type= table->timestamp_field_type;
/* The session variable settings can always be copied. */ /* Copy session variables. */
row->auto_increment_increment= thd->variables.auto_increment_increment; row->auto_increment_increment= thd->variables.auto_increment_increment;
row->auto_increment_offset= thd->variables.auto_increment_offset; row->auto_increment_offset= thd->variables.auto_increment_offset;
/* /* Copy the next forced auto increment value, if any. */
Next insert id must be set for the first value in a multi-row insert if ((forced_auto_inc= thd->auto_inc_intervals_forced.get_next()))
only. So clear it after the first use. Assume a multi-row insert. {
Since the user thread doesn't really execute the insert, row->forced_insert_id= forced_auto_inc->minimum();
thd->next_insert_id is left untouched between the rows. If we copy DBUG_PRINT("delayed", ("transmitting auto_inc: %lu",
the same insert id to every row of the multi-row insert, the delayed (ulong) row->forced_insert_id));
insert thread would copy this before inserting every row. Thus it }
tries to insert all rows with the same insert id. This fails on the
unique constraint. So just the first row would be really inserted.
*/
row->next_insert_id= thd->next_insert_id;
thd->next_insert_id= 0;
di->rows.push_back(row); di->rows.push_back(row);
di->stacked_inserts++; di->stacked_inserts++;
...@@ -2013,6 +2013,10 @@ pthread_handler_t handle_delayed_insert(void *arg) ...@@ -2013,6 +2013,10 @@ pthread_handler_t handle_delayed_insert(void *arg)
MYSQL_LOCK *lock=thd->lock; MYSQL_LOCK *lock=thd->lock;
thd->lock=0; thd->lock=0;
pthread_mutex_unlock(&di->mutex); pthread_mutex_unlock(&di->mutex);
/*
We need to release next_insert_id before unlocking. This is
enforced by handler::ha_external_lock().
*/
di->table->file->ha_release_auto_increment(); di->table->file->ha_release_auto_increment();
mysql_unlock_tables(thd, lock); mysql_unlock_tables(thd, lock);
di->group_count=0; di->group_count=0;
...@@ -2133,21 +2137,42 @@ bool delayed_insert::handle_inserts(void) ...@@ -2133,21 +2137,42 @@ bool delayed_insert::handle_inserts(void)
thd.start_time=row->start_time; thd.start_time=row->start_time;
thd.query_start_used=row->query_start_used; thd.query_start_used=row->query_start_used;
/* for the binlog, forget auto_increment ids generated by previous rows */ /*
// thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty(); To get the exact auto_inc interval to store in the binlog we must not
use values from the previous interval (of the previous rows).
*/
bool log_query= (row->log_query && row->query.str != NULL);
DBUG_PRINT("delayed", ("query: '%s' length: %u", row->query.str ?
row->query.str : "[NULL]", row->query.length));
if (row->query.str)
{
/*
This is the first value of an INSERT statement.
It is the right place to clear a forced insert_id.
This is usually done after the last value of an INSERT statement,
but we won't know this in the insert delayed thread. But before
the first value is sufficiently equivalent to after the last
value of the previous statement.
*/
table->file->ha_release_auto_increment();
thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty();
}
thd.first_successful_insert_id_in_prev_stmt= thd.first_successful_insert_id_in_prev_stmt=
row->first_successful_insert_id_in_prev_stmt; row->first_successful_insert_id_in_prev_stmt;
thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt= thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt=
row->stmt_depends_on_first_successful_insert_id_in_prev_stmt; row->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
table->timestamp_field_type= row->timestamp_field_type; table->timestamp_field_type= row->timestamp_field_type;
/* The session variable settings can always be copied. */ /* Copy the session variables. */
thd.variables.auto_increment_increment= row->auto_increment_increment; thd.variables.auto_increment_increment= row->auto_increment_increment;
thd.variables.auto_increment_offset= row->auto_increment_offset; thd.variables.auto_increment_offset= row->auto_increment_offset;
/* Next insert id must be used only if non-zero. */ /* Copy a forced insert_id, if any. */
if (row->next_insert_id) if (row->forced_insert_id)
thd.next_insert_id= row->next_insert_id; {
DBUG_PRINT("loop", ("next_insert_id: %lu", (ulong) thd.next_insert_id)); DBUG_PRINT("delayed", ("received auto_inc: %lu",
(ulong) row->forced_insert_id));
thd.force_one_auto_inc_interval(row->forced_insert_id);
}
info.ignore= row->ignore; info.ignore= row->ignore;
info.handle_duplicates= row->dup; info.handle_duplicates= row->dup;
...@@ -2170,20 +2195,6 @@ bool delayed_insert::handle_inserts(void) ...@@ -2170,20 +2195,6 @@ bool delayed_insert::handle_inserts(void)
info.error_count++; // Ignore errors info.error_count++; // Ignore errors
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
row->log_query = 0; row->log_query = 0;
/*
We must reset next_insert_id. Otherwise all following rows may
become duplicates. If write_record() failed on a duplicate and
next_insert_id would be left unchanged, the next rows would also
be tried with the same insert id and would fail. Since the end
of a multi-row statement is unknown here, all following rows in
the queue would be dropped, regardless which thread added them.
After the queue is used up, next_insert_id is cleared and the
next run will succeed. This could even happen if these come from
the same multi-row statement as the current queue contents. That
way it would look somewhat random which rows are rejected after
a duplicate.
*/
thd.next_insert_id= 0;
} }
if (using_ignore) if (using_ignore)
...@@ -2197,7 +2208,7 @@ bool delayed_insert::handle_inserts(void) ...@@ -2197,7 +2208,7 @@ bool delayed_insert::handle_inserts(void)
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
} }
if (row->log_query && row->query.str != NULL && mysql_bin_log.is_open()) if (log_query && mysql_bin_log.is_open())
{ {
/* /*
If the query has several rows to insert, only the first row will come If the query has several rows to insert, only the first row will come
......
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