List:Commits« Previous MessageNext Message »
From:guilhem Date:July 9 2006 10:50pm
Subject:bk commit into 5.1 tree (guilhem:1.2249)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of guilhem. When guilhem does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2006-07-09 22:50:02+02:00, guilhem@stripped +15 -0
  Merge gbichot3.local:/home/mysql_src/mysql-5.1-interval-move-next-insert-id
  into  gbichot3.local:/home/mysql_src/mysql-5.1
  MERGE: 1.2230.1.1

  mysql-test/extra/rpl_tests/rpl_insert_id.test@stripped, 2006-07-09 22:49:59+02:00,
guilhem@stripped +1 -1
    merge
    MERGE: 1.6.1.1

  mysql-test/r/rpl_insert_id.result@stripped, 2006-07-09 22:49:59+02:00,
guilhem@stripped +0 -0
    merge
    MERGE: 1.15.1.1

  sql/ha_federated.cc@stripped, 2006-07-09 21:54:14+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.62.1.1

  sql/ha_ndbcluster.cc@stripped, 2006-07-09 21:54:14+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.336.1.1

  sql/handler.cc@stripped, 2006-07-09 22:44:32+02:00, guilhem@stripped +0 -62
    will fix by hand
    MERGE: 1.247.1.1

  sql/handler.h@stripped, 2006-07-09 21:54:14+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.217.1.5

  sql/log_event.cc@stripped, 2006-07-09 21:54:14+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.230.1.1

  sql/set_var.cc@stripped, 2006-07-09 21:54:14+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.178.2.1

  sql/sql_class.cc@stripped, 2006-07-09 21:54:15+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.271.1.1

  sql/sql_class.h@stripped, 2006-07-09 21:54:15+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.304.2.1

  sql/sql_insert.cc@stripped, 2006-07-09 22:49:59+02:00, guilhem@stripped +6 -8
    merge
    MERGE: 1.206.2.1

  sql/sql_parse.cc@stripped, 2006-07-09 21:54:15+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.565.1.1

  sql/sql_select.cc@stripped, 2006-07-09 21:54:15+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.417.1.1

  sql/sql_table.cc@stripped, 2006-07-09 21:54:16+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.350.1.2

  sql/sql_update.cc@stripped, 2006-07-09 21:54:16+02:00, guilhem@stripped +0 -0
    Auto merged
    MERGE: 1.192.1.3

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	guilhem
# Host:	gbichot3.local
# Root:	/home/mysql_src/mysql-5.1/RESYNC

--- 1.223/sql/handler.h	2006-07-09 22:50:10 +02:00
+++ 1.224/sql/handler.h	2006-07-09 22:50:10 +02:00
@@ -906,16 +906,37 @@
   uint ref_length;
   FT_INFO *ft_handler;
   enum {NONE=0, INDEX, RND} inited;
-  bool  auto_increment_column_changed;
   bool implicit_emptied;                /* Can be !=0 only if HEAP */
   const COND *pushed_cond;
+  /*
+    next_insert_id is the next value which should be inserted into the
+    auto_increment column: in a inserting-multi-row statement (like INSERT
+    SELECT), for the first row where the autoinc value is not specified by the
+    statement, get_auto_increment() called and asked to generate a value,
+    next_insert_id is set to the next value, then for all other rows
+    next_insert_id is used (and increased each time) without calling
+    get_auto_increment().
+  */
+  ulonglong next_insert_id;
+  /*
+    insert id for the current row (*autogenerated*; if not
+    autogenerated, it's 0).
+    At first successful insertion, this variable is stored into
+    THD::first_successful_insert_id_in_cur_stmt.
+  */
+  ulonglong insert_id_for_cur_row;
+  /*
+    Interval returned by get_auto_increment() and being consumed by the
+    inserter.
+  */
+  Discrete_interval auto_inc_interval_for_cur_row;
 
   handler(const handlerton *ht_arg, TABLE_SHARE *share_arg)
     :table_share(share_arg), estimation_rows_to_insert(0), ht(ht_arg),
     ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
     ref_length(sizeof(my_off_t)),
     ft_handler(0), inited(NONE), implicit_emptied(0),
-    pushed_cond(NULL)
+    pushed_cond(NULL), next_insert_id(0), insert_id_for_cur_row(0)
     {}
   virtual ~handler(void)
   {
@@ -1248,9 +1269,30 @@
                                   ulonglong nb_desired_values,
                                   ulonglong *first_value,
                                   ulonglong *nb_reserved_values);
+private:
   virtual void release_auto_increment() { return; };
-  virtual void restore_auto_increment();
-
+public:
+  void ha_release_auto_increment();
+  void set_next_insert_id(ulonglong id)
+  {
+    DBUG_PRINT("info",("auto_increment: next value %lu", (ulong)id));
+    next_insert_id= id;
+  }
+  void restore_auto_increment(ulonglong prev_insert_id)
+  {
+    /*
+      Insertion of a row failed, re-use the lastly generated auto_increment
+      id, for the next row. This is achieved by resetting next_insert_id to
+      what it was before the failed insertion (that old value is provided by
+      the caller). If that value was 0, it was the first row of the INSERT;
+      then if insert_id_for_cur_row contains 0 it means no id was generated
+      for this first row, so no id was generated since the INSERT started, so
+      we should set next_insert_id to 0; if insert_id_for_cur_row is not 0, it
+      is the generated id of the first and failed row, so we use it.
+    */
+    next_insert_id= (prev_insert_id > 0) ? prev_insert_id :
+      insert_id_for_cur_row;
+  }
   /*
     Reset the auto-increment counter to the given value, i.e. the next row
     inserted will get the given value. This is called e.g. after TRUNCATE

--- 1.231/sql/log_event.cc	2006-07-09 22:50:10 +02:00
+++ 1.232/sql/log_event.cc	2006-07-09 22:50:10 +02:00
@@ -1921,6 +1921,16 @@
   thd->query_length= thd->db_length =0;
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   close_thread_tables(thd);      
+  /*
+    As a disk space optimization, future masters will not log an event for
+    LAST_INSERT_ID() if that function returned 0 (and thus they will be able
+    to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt
+    variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the
+    resetting below we are ready to support that.
+  */
+  thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0;
+  thd->first_successful_insert_id_in_prev_stmt= 0;
+  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
   free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
   /*
     If there was an error we stop. Otherwise we increment positions. Note that
@@ -3396,11 +3406,11 @@
 {
   switch (type) {
   case LAST_INSERT_ID_EVENT:
-    thd->last_insert_id_used = 1;
-    thd->last_insert_id = val;
+    thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
+    thd->first_successful_insert_id_in_prev_stmt= val;
     break;
   case INSERT_ID_EVENT:
-    thd->next_insert_id = val;
+    thd->force_one_auto_inc_interval(val);
     break;
   }
   rli->inc_event_relay_log_pos();

--- 1.272/sql/sql_class.cc	2006-07-09 22:50:10 +02:00
+++ 1.273/sql/sql_class.cc	2006-07-09 22:50:10 +02:00
@@ -208,8 +208,12 @@
 #endif /*HAVE_ROW_BASED_REPLICATION*/
    global_read_lock(0), is_fatal_error(0),
    rand_used(0), time_zone_used(0),
-   last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
+   arg_of_last_insert_id_function(FALSE),
+   first_successful_insert_id_in_prev_stmt(0),
+   first_successful_insert_id_in_prev_stmt_for_binlog(0),
+   first_successful_insert_id_in_cur_stmt(0),
    in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
+   stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
    spcont(NULL)
 {
   stmt_arena= this;
@@ -224,7 +228,6 @@
   killed= NOT_KILLED;
   db_length= col_access=0;
   query_error= tmp_table_used= 0;
-  next_insert_id=last_insert_id=0;
   hash_clear(&handler_tables_hash);
   tmp_table=0;
   used_tables=0;
@@ -628,11 +631,26 @@
 
 void THD::cleanup_after_query()
 {
-  if (clear_next_insert_id)
+  /*
+    If in stored function or trigger, where statement-based binlogging logs
+    only the caller, the insert_id/last_insert_id stored in binlog must
+    describe their first values inside the routine or caller (the values when
+    they were first set). Otherwise (e.g. stored procedure) it must describe
+    their values for the current substatement.
+  */
+  if (!prelocked_mode)
   {
-    clear_next_insert_id= 0;
-    next_insert_id= 0;
+    stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+    auto_inc_intervals_in_cur_stmt_for_binlog.empty();
   }
+  if (first_successful_insert_id_in_cur_stmt > 0)
+  {
+    /* set what LAST_INSERT_ID() will return */
+    first_successful_insert_id_in_prev_stmt= 
+      first_successful_insert_id_in_cur_stmt;
+    first_successful_insert_id_in_cur_stmt= 0;
+  }
+  arg_of_last_insert_id_function= 0;
   /* Free Items that were created during this execution */
   free_items();
   /* Reset where. */
@@ -2139,18 +2157,16 @@
   backup->in_sub_stmt=     in_sub_stmt;
   backup->no_send_ok=      net.no_send_ok;
   backup->enable_slow_log= enable_slow_log;
-  backup->last_insert_id=  last_insert_id;
-  backup->next_insert_id=  next_insert_id;
-  backup->current_insert_id=  current_insert_id;
-  backup->insert_id_used=  insert_id_used;
-  backup->last_insert_id_used=  last_insert_id_used;
-  backup->clear_next_insert_id= clear_next_insert_id;
   backup->limit_found_rows= limit_found_rows;
   backup->examined_row_count= examined_row_count;
   backup->sent_row_count=   sent_row_count;
   backup->cuted_fields=     cuted_fields;
   backup->client_capabilities= client_capabilities;
   backup->savepoints= transaction.savepoints;
+  backup->first_successful_insert_id_in_prev_stmt= 
+    first_successful_insert_id_in_prev_stmt;
+  backup->first_successful_insert_id_in_cur_stmt= 
+    first_successful_insert_id_in_cur_stmt;
 
   if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
       !current_stmt_binlog_row_based)
@@ -2160,12 +2176,11 @@
   /* Disable result sets */
   client_capabilities &= ~CLIENT_MULTI_RESULTS;
   in_sub_stmt|= new_state;
-  next_insert_id= 0;
-  insert_id_used= 0;
   examined_row_count= 0;
   sent_row_count= 0;
   cuted_fields= 0;
   transaction.savepoints= 0;
+  first_successful_insert_id_in_cur_stmt= 0;
 
   /* Surpress OK packets in case if we will execute statements */
   net.no_send_ok= TRUE;
@@ -2193,12 +2208,10 @@
   in_sub_stmt=      backup->in_sub_stmt;
   net.no_send_ok=   backup->no_send_ok;
   enable_slow_log=  backup->enable_slow_log;
-  last_insert_id=   backup->last_insert_id;
-  next_insert_id=   backup->next_insert_id;
-  current_insert_id= backup->current_insert_id;
-  insert_id_used=   backup->insert_id_used;
-  last_insert_id_used= backup->last_insert_id_used;
-  clear_next_insert_id= backup->clear_next_insert_id;
+  first_successful_insert_id_in_prev_stmt= 
+    backup->first_successful_insert_id_in_prev_stmt;
+  first_successful_insert_id_in_cur_stmt= 
+    backup->first_successful_insert_id_in_cur_stmt;
   limit_found_rows= backup->limit_found_rows;
   sent_row_count=   backup->sent_row_count;
   client_capabilities= backup->client_capabilities;
@@ -2776,6 +2789,28 @@
   case THD::QUERY_TYPE_COUNT:
   default:
     DBUG_ASSERT(0 <= qtype && qtype < QUERY_TYPE_COUNT);
+  }
+  DBUG_RETURN(0);
+}
+
+bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
+                                 ulonglong incr)
+{
+  DBUG_ENTER("Discrete_intervals_list::append");
+  /* first, see if this can be merged with previous */
+  if ((head == NULL) || tail->merge_if_contiguous(start, val, incr))
+  {
+    /* it cannot, so need to add a new interval */
+    Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
+    if (unlikely(new_interval == NULL)) // out of memory
+      DBUG_RETURN(1);
+    DBUG_PRINT("info",("adding new auto_increment interval"));
+    if (head == NULL)
+      head= current= new_interval;
+    else
+      tail->next= new_interval;
+    tail= new_interval;
+    elements++;
   }
   DBUG_RETURN(0);
 }

--- 1.306/sql/sql_class.h	2006-07-09 22:50:10 +02:00
+++ 1.307/sql/sql_class.h	2006-07-09 22:50:10 +02:00
@@ -770,12 +770,14 @@
 {
 public:
   ulonglong options;
-  ulonglong last_insert_id, next_insert_id, current_insert_id;
+  ulonglong first_successful_insert_id_in_prev_stmt;
+  ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row;
+  Discrete_interval auto_inc_interval_for_cur_row;
   ulonglong limit_found_rows;
   ha_rows    cuted_fields, sent_row_count, examined_row_count;
   ulong client_capabilities;
   uint in_sub_stmt;
-  bool enable_slow_log, insert_id_used, clear_next_insert_id;
+  bool enable_slow_log;
   bool last_insert_id_used;
   my_bool no_send_ok;
   SAVEPOINT *savepoints;
@@ -1071,24 +1073,136 @@
     Note: in the parser, stmt_arena == thd, even for PS/SP.
   */
   Query_arena *stmt_arena;
+  /* Tells if LAST_INSERT_ID(#) was called for the current statement */
+  bool arg_of_last_insert_id_function;
   /*
-    next_insert_id is set on SET INSERT_ID= #. This is used as the next
-    generated auto_increment value in handler.cc
+    ALL OVER THIS FILE, "insert_id" means "*automatically generated* value for
+    insertion into an auto_increment column".
   */
-  ulonglong  next_insert_id;
-  /* Remember last next_insert_id to reset it if something went wrong */
-  ulonglong  prev_insert_id;
   /*
-    The insert_id used for the last statement or set by SET LAST_INSERT_ID=#
-    or SELECT LAST_INSERT_ID(#).  Used for binary log and returned by
-    LAST_INSERT_ID()
+    This is the first autogenerated insert id which was *successfully*
+    inserted by the previous statement (exactly, if the previous statement
+    didn't successfully insert an autogenerated insert id, then it's the one
+    of the statement before, etc).
+    It can also be set by SET LAST_INSERT_ID=# or SELECT LAST_INSERT_ID(#).
+    It is returned by LAST_INSERT_ID().
+  */
+  ulonglong  first_successful_insert_id_in_prev_stmt;
+  /*
+    Variant of the above, used for storing in statement-based binlog. The
+    difference is that the one above can change as the execution of a stored
+    function progresses, while the one below is set once and then does not
+    change (which is the value which statement-based binlog needs).
+  */
+  ulonglong  first_successful_insert_id_in_prev_stmt_for_binlog;
+  /*
+    This is the first autogenerated insert id which was *successfully*
+    inserted by the current statement. It is maintained only to set
+    first_successful_insert_id_in_prev_stmt when statement ends.
+  */
+  ulonglong  first_successful_insert_id_in_cur_stmt;
+  /*
+    We follow this logic:
+    - when stmt starts, first_successful_insert_id_in_prev_stmt contains the
+    first insert id successfully inserted by the previous stmt.
+    - as stmt makes progress, handler::insert_id_for_cur_row changes; every
+    time get_auto_increment() is called, auto_inc_intervals_for_binlog is
+    augmented with the reserved interval (if statement-based binlogging).
+    - at first successful insertion of an autogenerated value,
+    first_successful_insert_id_in_cur_stmt is set to
+    handler::insert_id_for_cur_row.
+    - when stmt goes to binlog, auto_inc_intervals_for_binlog is
+    binlogged if non-empty.
+    - when stmt ends, first_successful_insert_id_in_prev_stmt is set to
+    first_successful_insert_id_in_cur_stmt.
+  */
+  /*
+    stmt_depends_on_first_successful_insert_id_in_prev_stmt is set when
+    LAST_INSERT_ID() is used by a statement.
+    If it is set, first_successful_insert_id_in_prev_stmt_for_binlog will be
+    stored in the statement-based binlog.
+    This variable is CUMULATIVE along the execution of a stored function or
+    trigger: if one substatement sets it to 1 it will stay 1 until the
+    function/trigger ends, thus making sure that
+    first_successful_insert_id_in_prev_stmt_for_binlog does not change anymore
+    and is propagated to the caller for binlogging.
+  */
+  bool       stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+  /*
+    List of auto_increment intervals reserved by the thread so far, for
+    storage in the statement-based binlog.
+    Note that its minimum is not first_successful_insert_id_in_cur_stmt:
+    assuming a table with an autoinc column, and this happens:
+    INSERT INTO ... VALUES(3);
+    SET INSERT_ID=3; INSERT IGNORE ... VALUES (NULL);
+    then the latter INSERT will insert no rows
+    (first_successful_insert_id_in_cur_stmt == 0), but storing "INSERT_ID=3"
+    in the binlog is still needed; the list's minimum will contain 3.
+  */
+  Discrete_intervals_list auto_inc_intervals_in_cur_stmt_for_binlog;
+  /* Used by replication and SET INSERT_ID */
+  Discrete_intervals_list auto_inc_intervals_forced;
+  /*
+    There is BUG#19630 where statement-based replication of stored
+    functions/triggers with two auto_increment columns breaks.
+    We however ensure that it works when there is 0 or 1 auto_increment
+    column; our rules are
+    a) on master, while executing a top statement involving substatements,
+    first top- or sub- statement to generate auto_increment values wins the
+    exclusive right to write them to binlog (so the losers won't write their
+    values to binlog).
+    b) on slave, while replicating a top statement involving substatements,
+    first top- or sub- statement to need to read auto_increment values from
+    the master's binlog wins the exclusive right to read them (so the losers
+    won't read their values from binlog but instead generate on their own).
+    a) implies that we mustn't backup/restore
+    auto_inc_intervals_in_cur_stmt_for_binlog.
+    b) implies that we mustn't backup/restore auto_inc_intervals_forced.
+
+    If there are more than 1 auto_increment columns, then intervals for
+    different columns may mix into the
+    auto_inc_intervals_in_cur_stmt_for_binlog list, which is logically wrong,
+    but there is no point in preventing this mixing by preventing intervals
+    from the secondly inserted column to come into the list, as such
+    prevention would be wrong too.
+    What will happen in the case of
+    INSERT INTO t1 (auto_inc) VALUES(NULL);
+    where t1 has a trigger which inserts into an auto_inc column of t2, is
+    that in binlog we'll store the interval of t1 and the interval of t2 (when
+    we store intervals, soon), then in slave, t1 will use both intervals, t2
+    will use none; if t1 inserts the same number of rows as on master,
+    normally the 2nd interval will not be used by t1, which is fine. t2's
+    values will be wrong if t2's internal auto_increment counter is different
+    from what it was on master (which is likely). In 5.1, in mixed binlogging
+    mode, row-based binlogging is used for such cases where two
+    auto_increment columns are inserted.
   */
-  ulonglong  last_insert_id;
+  inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id)
+  {
+    if (first_successful_insert_id_in_cur_stmt == 0)
+      first_successful_insert_id_in_cur_stmt= id;
+  }
+  inline ulonglong read_first_successful_insert_id_in_prev_stmt(void)
+  {
+    if (!stmt_depends_on_first_successful_insert_id_in_prev_stmt)
+    {
+      /* It's the first time we read it */
+      first_successful_insert_id_in_prev_stmt_for_binlog=
+        first_successful_insert_id_in_prev_stmt;
+      stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
+    }
+    return first_successful_insert_id_in_prev_stmt;
+  }
   /*
-    Set to the first value that LAST_INSERT_ID() returned for the last
-    statement.  When this is set, last_insert_id_used is set to true.
+    Used by Intvar_log_event::exec_event() and by "SET INSERT_ID=#"
+    (mysqlbinlog). We'll soon add a variant which can take many intervals in
+    argument.
   */
-  ulonglong  current_insert_id;
+  inline void force_one_auto_inc_interval(ulonglong next_id)
+  {
+    auto_inc_intervals_forced.append(next_id, ULONGLONG_MAX, 0);
+  }
+
   ulonglong  limit_found_rows;
   ulonglong  options;           /* Bitmap of states */
   longlong   row_count_func;	/* For the ROW_COUNT() function */
@@ -1157,7 +1271,6 @@
   bool       last_cuted_field;
   bool	     no_errors, password, is_fatal_error;
   bool	     query_start_used, rand_used, time_zone_used;
-  bool	     last_insert_id_used,insert_id_used, clear_next_insert_id;
   bool	     in_lock_tables;
   bool       query_error, bootstrap, cleanup_done;
   bool	     tmp_table_used;
@@ -1185,9 +1298,10 @@
   /* Used by the sys_var class to store temporary values */
   union
   {
-    my_bool my_bool_value;
-    long    long_value;
-    ulong   ulong_value;
+    my_bool   my_bool_value;
+    long      long_value;
+    ulong     ulong_value;
+    ulonglong ulonglong_value;
   } sys_var_tmp;
   
   struct {
@@ -1288,20 +1402,6 @@
   inline void	end_time()    { time(&start_time); }
   inline void	set_time(time_t t) { time_after_lock=start_time=user_time=t; }
   inline void	lock_time()   { time(&time_after_lock); }
-  inline void	insert_id(ulonglong id_arg)
-  {
-    last_insert_id= id_arg;
-    insert_id_used=1;
-  }
-  inline ulonglong insert_id(void)
-  {
-    if (!last_insert_id_used)
-    {
-      last_insert_id_used=1;
-      current_insert_id=last_insert_id;
-    }
-    return last_insert_id;
-  }
   inline ulonglong found_rows(void)
   {
     return limit_found_rows;
@@ -1617,7 +1717,7 @@
   TABLE_LIST *table_list;
   TABLE *table;
   List<Item> *fields;
-  ulonglong last_insert_id;
+  ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not
   COPY_INFO info;
   bool insert_into_view;
 

--- 1.212/sql/sql_insert.cc	2006-07-09 22:50:10 +02:00
+++ 1.213/sql/sql_insert.cc	2006-07-09 22:50:10 +02:00
@@ -411,7 +411,6 @@
   table->next_number_field=table->found_next_number_field;
 
   error=0;
-  id=0;
   thd->proc_info="update";
   if (duplic != DUP_ERROR || ignore)
     table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
@@ -518,16 +517,6 @@
     else
 #endif
       error=write_record(thd, table ,&info);
-    /*
-      If auto_increment values are used, save the first one
-       for LAST_INSERT_ID() and for the update log.
-       We can't use insert_id() as we don't want to touch the
-       last_insert_id_used flag.
-    */
-    if (! id && thd->insert_id_used)
-    {						// Get auto increment value
-      id= thd->last_insert_id;
-    }
     if (error)
       break;
     thd->row_count++;
@@ -535,6 +524,7 @@
 
   free_underlaid_joins(thd, &thd->lex->select_lex);
   joins_freed= TRUE;
+  table->file->ha_release_auto_increment();
 
   /*
     Now all rows are inserted.  Time to update logs and sends response to
@@ -545,7 +535,6 @@
   {
     if (!error)
     {
-      id=0;					// No auto_increment id
       info.copied=values_list.elements;
       end_delayed_insert(thd);
     }
@@ -559,11 +548,6 @@
       table->file->print_error(my_errno,MYF(0));
       error=1;
     }
-    if (id && values_list.elements != 1)
-      thd->insert_id(id);			// For update log
-    else if (table->next_number_field && info.copied)
-      id=table->next_number_field->val_int();	// Return auto_increment value
-
     transactional_table= table->file->has_transactions();
 
     if ((changed= (info.copied || info.deleted || info.updated)))
@@ -612,18 +596,27 @@
     }
   }
   thd->proc_info="end";
+  /*
+    We'll report to the client this id:
+    - if the table contains an autoincrement column and we successfully
+    inserted an autogenerated value, the autogenerated value.
+    - if the table contains no autoincrement column and LAST_INSERT_ID(X) was
+    called, X.
+    - if the table contains an autoincrement column, and some rows were
+    inserted, the id of the last "inserted" row (if IGNORE, that value may not
+    have been really inserted but ignored).
+  */
+  id= (thd->first_successful_insert_id_in_cur_stmt > 0) ?
+    thd->first_successful_insert_id_in_cur_stmt :
+    (thd->arg_of_last_insert_id_function ?
+     thd->first_successful_insert_id_in_prev_stmt :
+     ((table->next_number_field && info.copied) ?
+     table->next_number_field->val_int() : 0));
   table->next_number_field=0;
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;
-  thd->next_insert_id=0;			// Reset this if wrongly used
   if (duplic != DUP_ERROR || ignore)
     table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
 
-  /* Reset value of LAST_INSERT_ID if no rows where inserted */
-  if (!info.copied && thd->insert_id_used)
-  {
-    thd->insert_id(0);
-    id=0;
-  }
   if (error)
     goto abort;
   if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
@@ -645,8 +638,6 @@
     thd->row_count_func= info.copied+info.deleted+info.updated;
     ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
   }
-  if (table != NULL)
-    table->file->release_auto_increment();
   thd->abort_on_warning= 0;
   DBUG_RETURN(FALSE);
 
@@ -656,7 +647,7 @@
     end_delayed_insert(thd);
 #endif
   if (table != NULL)
-    table->file->release_auto_increment();
+    table->file->ha_release_auto_increment();
   if (!joins_freed)
     free_underlaid_joins(thd, &thd->lex->select_lex);
   thd->abort_on_warning= 0;
@@ -965,6 +956,8 @@
   int error, trg_error= 0;
   char *key=0;
   MY_BITMAP *save_read_set, *save_write_set;
+  ulonglong prev_insert_id= table->file->next_insert_id;
+  ulonglong insert_id_for_cur_row= 0;
   DBUG_ENTER("write_record");
 
   info->records++;
@@ -977,6 +970,17 @@
     while ((error=table->file->ha_write_row(table->record[0])))
     {
       uint key_nr;
+      /*
+        If we do more than one iteration of this loop, from the second one the
+        row will have an explicit value in the autoinc field, which was set at
+        the first call of handler::update_auto_increment(). So we must save
+        the autogenerated value to avoid thd->insert_id_for_cur_row to become
+        0.
+      */
+      if (table->file->insert_id_for_cur_row > 0)
+        insert_id_for_cur_row= table->file->insert_id_for_cur_row;
+      else
+        table->file->insert_id_for_cur_row= insert_id_for_cur_row;
       bool is_duplicate_key_error;
       if (table->file->is_fatal_error(error, HA_CHECK_DUP))
 	goto err;
@@ -1007,7 +1011,7 @@
       if (info->handle_duplicates == DUP_REPLACE &&
           table->next_number_field &&
           key_nr == table->s->next_number_index &&
-	  table->file->auto_increment_column_changed)
+	  (insert_id_for_cur_row > 0))
 	goto err;
       if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
       {
@@ -1078,15 +1082,21 @@
           goto err;
 	}
         info->updated++;
-
+        /*
+          If ON DUP KEY UPDATE updates a row instead of inserting one, and
+          there is an auto_increment column, then SELECT LAST_INSERT_ID()
+          returns the id of the updated row:
+        */
         if (table->next_number_field)
-         
table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int());
-
+        {
+          longlong field_val= table->next_number_field->val_int();
+          thd->record_first_successful_insert_id_in_cur_stmt(field_val);
+          table->file->adjust_next_insert_id_after_explicit_value(field_val);
+        }
         trg_error= (table->triggers &&
                     table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
                                                       TRG_ACTION_AFTER, TRUE));
-        info->copied++;
-        goto ok_or_after_trg_err;
+        info->copiedgoto ok_or_after_trg_err;
       }
       else /* DUP_REPLACE */
       {
@@ -1114,6 +1124,7 @@
 					        table->record[0])))
             goto err;
           info->deleted++;
+         
thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
           /*
             Since we pretend that we have done insert we should call
             its after triggers.
@@ -1142,6 +1153,7 @@
         }
       }
     }
+   
thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
     /*
       Restore column maps if they where replaced during an duplicate key
       problem.
@@ -1155,12 +1167,13 @@
     if (!info->ignore ||
         table->file->is_fatal_error(error, HA_CHECK_DUP))
       goto err;
-    table->file->restore_auto_increment();
+    table->file->restore_auto_increment(prev_insert_id);
     goto ok_or_after_trg_err;
   }
 
 after_trg_n_copied_inc:
   info->copied++;
+ 
thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
   trg_error= (table->triggers &&
               table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
                                                 TRG_ACTION_AFTER, TRUE));
@@ -1243,8 +1256,9 @@
   char *record;
   enum_duplicates dup;
   time_t start_time;
-  bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query;
-  ulonglong last_insert_id;
+  bool query_start_used, ignore, log_query;
+  bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+  ulonglong first_successful_insert_id_in_prev_stmt;
   timestamp_auto_set_type timestamp_field_type;
   LEX_STRING query;
 
@@ -1655,9 +1669,16 @@
   memcpy(row->record, table->record[0], table->s->reclength);
   row->start_time=		thd->start_time;
   row->query_start_used=	thd->query_start_used;
-  row->last_insert_id_used=	thd->last_insert_id_used;
-  row->insert_id_used=		thd->insert_id_used;
-  row->last_insert_id=		thd->last_insert_id;
+  /*
+    those are for the binlog: LAST_INSERT_ID() has been evaluated at this
+    time, so record does not need it, but statement-based binlogging of the
+    INSERT will need when the row is actually inserted.
+    As for SET INSERT_ID, DELAYED does not honour it (BUG#20830).
+  */
+  row->stmt_depends_on_first_successful_insert_id_in_prev_stmt=
+    thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+  row->first_successful_insert_id_in_prev_stmt=
+    thd->first_successful_insert_id_in_prev_stmt;
   row->timestamp_field_type=    table->timestamp_field_type;
 
   di->rows.push_back(row);
@@ -1911,6 +1932,7 @@
       MYSQL_LOCK *lock=thd->lock;
       thd->lock=0;
       pthread_mutex_unlock(&di->mutex);
+      di->table->file->ha_release_auto_increment();
       mysql_unlock_tables(thd, lock);
       di->group_count=0;
       pthread_mutex_lock(&di->mutex);
@@ -2023,13 +2045,6 @@
     table->file->extra(HA_EXTRA_WRITE_CACHE);
   pthread_mutex_lock(&mutex);
 
-  /* Reset auto-increment cacheing */
-  if (thd.clear_next_insert_id)
-  {
-    thd.next_insert_id= 0;
-    thd.clear_next_insert_id= 0;
-  }
-
   while ((row=rows.get()))
   {
     stacked_inserts--;
@@ -2038,9 +2053,12 @@
 
     thd.start_time=row->start_time;
     thd.query_start_used=row->query_start_used;
-    thd.last_insert_id=row->last_insert_id;
-    thd.last_insert_id_used=row->last_insert_id_used;
-    thd.insert_id_used=row->insert_id_used;
+    /* for the binlog, forget auto_increment ids generated by previous rows */
+    thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+    thd.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= 
+      row->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
     table->timestamp_field_type= row->timestamp_field_type;
 
     info.ignore= row->ignore;
@@ -2232,7 +2250,7 @@
                              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),
    insert_into_view(table_list_par && table_list_par->view != 0)
 {
   bzero((char*) &info,sizeof(info));
@@ -2441,15 +2459,20 @@
     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->first_successful_insert_id_in_cur_stmt == 0) // optimization
+        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->insert_id();
     }
   }
-  table->file->release_auto_increment();
+  table->file->ha_release_auto_increment();
   DBUG_RETURN(error);
 }
 
@@ -2511,8 +2534,6 @@
   {
     if (!table->file->has_transactions())
     {
-      if (last_insert_id)
-        thd->insert_id(last_insert_id);		// For binary log
       if (mysql_bin_log.is_open())
       {
         thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length,
@@ -2532,6 +2553,7 @@
 bool select_insert::send_eof()
 {
   int error,error2;
+  ulonglong id;
   DBUG_ENTER("select_insert::send_eof");
 
   error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0;
@@ -2557,8 +2579,6 @@
       thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
    }
 
-  if (last_insert_id)
-    thd->insert_id(last_insert_id);		// For binary log
   /*
     Write to binlog before commiting transaction.  No statement will
     be written by the binlog_query() below in RBR mode.  All the
@@ -2588,7 +2608,13 @@
     sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
 	    (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields);
   thd->row_count_func= info.copied+info.deleted+info.updated;
-  ::send_ok(thd, (ulong) thd->row_count_func, last_insert_id, buff);
+
+  id= (thd->first_successful_insert_id_in_cur_stmt > 0) ?
+    thd->first_successful_insert_id_in_cur_stmt :
+    (thd->arg_of_last_insert_id_function ?
+     thd->first_successful_insert_id_in_prev_stmt :
+     (info.copied ? autoinc_value_of_last_inserted_row : 0));
+  ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
   DBUG_RETURN(0);
 }
 

--- 1.566/sql/sql_parse.cc	2006-07-09 22:50:10 +02:00
+++ 1.567/sql/sql_parse.cc	2006-07-09 22:50:10 +02:00
@@ -3343,8 +3343,9 @@
     res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
 		      lex->update_list, lex->value_list,
                       lex->duplicates, lex->ignore);
+    /* do not show last insert ID if VIEW does not have auto_inc */
     if (first_table->view && !first_table->contain_auto_increment)
-      thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
+      thd->first_successful_insert_id_in_cur_stmt= 0;
     break;
   }
   case SQLCOM_REPLACE_SELECT:
@@ -3396,9 +3397,9 @@
       /* revert changes for SP */
       select_lex->table_list.first= (byte*) first_table;
     }
-
+    /* do not show last insert ID if VIEW does not have auto_inc */
     if (first_table->view && !first_table->contain_auto_increment)
-      thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
+      thd->first_successful_insert_id_in_cur_stmt= 0;
     break;
   }
   case SQLCOM_TRUNCATE:
@@ -5799,6 +5800,7 @@
  DESCRIPTION
    This needs to be called before execution of every statement
    (prepared or conventional).
+   It is not called by substatements of routines.
 
  TODO
    Make it a method of THD and align its name with the rest of
@@ -5809,9 +5811,12 @@
 void mysql_reset_thd_for_next_command(THD *thd)
 {
   DBUG_ENTER("mysql_reset_thd_for_next_command");
+  DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
   thd->free_list= 0;
   thd->select_number= 1;
-  thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
+  thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 
+    thd->query_start_used= 0;
   thd->is_fatal_error= thd->time_zone_used= 0;
   thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
                           SERVER_QUERY_NO_INDEX_USED |

--- 1.418/sql/sql_select.cc	2006-07-09 22:50:10 +02:00
+++ 1.419/sql/sql_select.cc	2006-07-09 22:50:10 +02:00
@@ -7895,7 +7895,7 @@
       Field *field=((Item_field*) args[0])->field;
       if (field->flags & AUTO_INCREMENT_FLAG &&
!field->table->maybe_null &&
 	  (thd->options & OPTION_AUTO_IS_NULL) &&
-	  thd->insert_id())
+	  (thd->first_successful_insert_id_in_prev_stmt > 0))
       {
 #ifdef HAVE_QUERY_CACHE
 	query_cache_abort(&thd->net);
@@ -7903,7 +7903,7 @@
 	COND *new_cond;
 	if ((new_cond= new Item_func_eq(args[0],
 					new Item_int("last_insert_id()",
-						     thd->insert_id(),
+                                                    
thd->read_first_successful_insert_id_in_prev_stmt(),
 						     21))))
 	{
 	  cond=new_cond;
@@ -7914,7 +7914,11 @@
           */
 	  cond->fix_fields(thd, &cond);
 	}
-	thd->insert_id(0);		// Clear for next request
+        /*
+          IS NULL should be mapped to LAST_INSERT_ID only for first row, so
+          clear for next row
+        */
+	thd->first_successful_insert_id_in_prev_stmt= 0;
       }
       /* fix to replace 'NULL' dates with '0' (shreeve@stripped) */
       else if (((field->type() == FIELD_TYPE_DATE) ||

--- 1.353/sql/sql_table.cc	2006-07-09 22:50:10 +02:00
+++ 1.354/sql/sql_table.cc	2006-07-09 22:50:10 +02:00
@@ -4953,7 +4953,6 @@
   char path[FN_REFLEN];
   char reg_path[FN_REFLEN+1];
   ha_rows copied,deleted;
-  ulonglong next_insert_id;
   uint db_create_options, used_fields;
   handlerton *old_db_type, *new_db_type;
   HA_CREATE_INFO *create_info;
@@ -5773,7 +5772,6 @@
   thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
   thd->cuted_fields=0L;
   thd->proc_info="copy to tmp table";
-  next_insert_id=thd->next_insert_id;		// Remember for logging
   copied=deleted=0;
   if (new_table && !(new_table->file->ha_table_flags() &
HA_NO_COPY_ON_ALTER))
   {
@@ -5784,7 +5782,6 @@
 				   handle_duplicates, ignore,
 				   order_num, order, &copied, &deleted);
   }
-  thd->last_insert_id=next_insert_id;		// Needed for correct log
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;
 
   /* If we did not need to copy, we might still need to add/drop indexes. */
@@ -6214,6 +6211,7 @@
   ha_rows examined_rows;
   bool auto_increment_field_copied= 0;
   ulong save_sql_mode;
+  ulonglong prev_insert_id;
   DBUG_ENTER("copy_data_between_tables");
 
   /*
@@ -6320,6 +6318,7 @@
     {
       copy_ptr->do_copy(copy_ptr);
     }
+    prev_insert_id= to->file->next_insert_id;
     if ((error=to->file->ha_write_row((byte*) to->record[0])))
     {
       if (!ignore || handle_duplicates != DUP_ERROR ||
@@ -6343,7 +6342,7 @@
 	to->file->print_error(error,MYF(0));
 	break;
       }
-      to->file->restore_auto_increment();
+      to->file->restore_auto_increment(prev_insert_id);
       delete_count++;
     }
     else
@@ -6377,6 +6376,7 @@
   free_io_cache(from);
   *copied= found_count;
   *deleted=delete_count;
+  to->file->ha_release_auto_increment();
   if (to->file->ha_external_lock(thd,F_UNLCK))
     error=1;
   DBUG_RETURN(error > 0 ? -1 : 0);

--- 1.197/sql/sql_update.cc	2006-07-09 22:50:10 +02:00
+++ 1.198/sql/sql_update.cc	2006-07-09 22:50:10 +02:00
@@ -135,7 +135,8 @@
   SQL_SELECT	*select;
   READ_RECORD	info;
   SELECT_LEX    *select_lex= &thd->lex->select_lex;
-  bool need_reopen;
+  bool          need_reopen;
+  ulonglong     id;
   DBUG_ENTER("mysql_update");
 
   for ( ; ; )
@@ -676,6 +677,10 @@
     thd->lock=0;
   }
 
+  /* If LAST_INSERT_ID(X) was used, report X */
+  id= thd->arg_of_last_insert_id_function ?
+    thd->first_successful_insert_id_in_prev_stmt : 0;
+
   if (error < 0)
   {
     char buff[STRING_BUFFER_USUAL_SIZE];
@@ -683,8 +688,7 @@
 	    (ulong) thd->cuted_fields);
     thd->row_count_func=
       (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
-    send_ok(thd, (ulong) thd->row_count_func,
-	    thd->insert_id_used ? thd->insert_id() : 0L,buff);
+    send_ok(thd, (ulong) thd->row_count_func, id, buff);
     DBUG_PRINT("info",("%d records updated",updated));
   }
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;		/* calc cuted fields */
@@ -1634,6 +1638,7 @@
 bool multi_update::send_eof()
 {
   char buff[STRING_BUFFER_USUAL_SIZE];
+  ulonglong id;
   thd->proc_info="updating reference tables";
 
   /* Does updates for the last n - 1 tables, returns 0 if ok */
@@ -1686,12 +1691,12 @@
     return TRUE;
   }
 
-
+  id= thd->arg_of_last_insert_id_function ?
+    thd->first_successful_insert_id_in_prev_stmt : 0;
   sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated,
 	  (ulong) thd->cuted_fields);
   thd->row_count_func=
     (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
-  ::send_ok(thd, (ulong) thd->row_count_func,
-	    thd->insert_id_used ? thd->insert_id() : 0L,buff);
+  ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
   return FALSE;
 }

--- 1.7/mysql-test/extra/rpl_tests/rpl_insert_id.test	2006-07-09 22:50:10 +02:00
+++ 1.8/mysql-test/extra/rpl_tests/rpl_insert_id.test	2006-07-09 22:50:10 +02:00
@@ -170,7 +170,70 @@
 
 drop function bug15728;
 drop function bug15728_insert;
+drop table t1, t2;
 drop procedure foo;
+
+# test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in
+# auto_increment breaks binlog
+
+create table t1 (n int primary key auto_increment not null,
+b int, unique(b));
+
+# First, test that we do not call restore_auto_increment() too early
+# in write_record():
+set sql_log_bin=0;
+insert into t1 values(null,100);
+replace into t1 values(null,50),(null,100),(null,150);
+select * from t1 order by n;
+truncate table t1;
+set sql_log_bin=1;
+
+insert into t1 values(null,100);
+select * from t1 order by n;
+sync_slave_with_master;
+# make slave's table autoinc counter bigger
+insert into t1 values(null,200),(null,300);
+delete from t1 where b <> 100;
+# check that slave's table content is identical to master
+select * from t1 order by n;
+# only the auto_inc counter differs.
+
+connection master;
+replace into t1 values(null,100),(null,350);
+select * from t1 order by n;
+sync_slave_with_master;
+select * from t1 order by n;
+
+# Same test as for REPLACE, but for ON DUPLICATE KEY UPDATE
+
+# We first check that if we update a row using a value larger than the
+# table's counter, the counter for next row is bigger than the
+# after-value of the updated row.
+connection master;
+insert into t1 values (NULL,400),(3,500),(NULL,600) on duplicate key UPDATE n=1000;
+select * from t1 order by n;
+sync_slave_with_master;
+select * from t1 order by n;
+
+# and now test for the bug:
+connection master;
+drop table t1;
+create table t1 (n int primary key auto_increment not null,
+b int, unique(b));
+insert into t1 values(null,100);
+select * from t1 order by n;
+sync_slave_with_master;
+insert into t1 values(null,200),(null,300);
+delete from t1 where b <> 100;
+select * from t1 order by n;
+
+connection master;
+insert into t1 values(null,100),(null,350) on duplicate key update n=2;
+select * from t1 order by n;
+sync_slave_with_master;
+select * from t1 order by n;
+
+connection master;
 drop table t1;
 
 # End of 5.0 tests

--- 1.16/mysql-test/r/rpl_insert_id.result	2006-07-09 22:50:10 +02:00
+++ 1.17/mysql-test/r/rpl_insert_id.result	2006-07-09 22:50:10 +02:00
@@ -190,3 +190,68 @@
 8	0
 drop table t1, t2;
 drop function insid;
+create table t1 (n int primary key auto_increment not null,
+b int, unique(b));
+set sql_log_bin=0;
+insert into t1 values(null,100);
+replace into t1 values(null,50),(null,100),(null,150);
+select * from t1 order by n;
+n	b
+2	50
+3	100
+4	150
+truncate table t1;
+set sql_log_bin=1;
+insert into t1 values(null,100);
+select * from t1 order by n;
+n	b
+1	100
+insert into t1 values(null,200),(null,300);
+delete from t1 where b <> 100;
+select * from t1 order by n;
+n	b
+1	100
+replace into t1 values(null,100),(null,350);
+select * from t1 order by n;
+n	b
+2	100
+3	350
+select * from t1 order by n;
+n	b
+2	100
+3	350
+insert into t1 values (NULL,400),(3,500),(NULL,600) on duplicate key UPDATE n=1000;
+select * from t1 order by n;
+n	b
+2	100
+4	400
+1000	350
+1001	600
+select * from t1 order by n;
+n	b
+2	100
+4	400
+1000	350
+1001	600
+drop table t1;
+create table t1 (n int primary key auto_increment not null,
+b int, unique(b));
+insert into t1 values(null,100);
+select * from t1 order by n;
+n	b
+1	100
+insert into t1 values(null,200),(null,300);
+delete from t1 where b <> 100;
+select * from t1 order by n;
+n	b
+1	100
+insert into t1 values(null,100),(null,350) on duplicate key update n=2;
+select * from t1 order by n;
+n	b
+2	100
+3	350
+select * from t1 order by n;
+n	b
+2	100
+3	350
+drop table t1;

--- 1.180/sql/set_var.cc	2006-07-09 22:50:10 +02:00
+++ 1.181/sql/set_var.cc	2006-07-09 22:50:10 +02:00
@@ -2794,7 +2794,8 @@
 
 bool sys_var_last_insert_id::update(THD *thd, set_var *var)
 {
-  thd->insert_id(var->save_result.ulonglong_value);
+  thd->first_successful_insert_id_in_prev_stmt= 
+    var->save_result.ulonglong_value;
   return 0;
 }
 
@@ -2802,14 +2803,19 @@
 byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
 					LEX_STRING *base)
 {
-  thd->sys_var_tmp.long_value= (long) thd->insert_id();
-  return (byte*) &thd->last_insert_id;
+  /*
+    this tmp var makes it robust againt change of type of 
+    read_first_successful_insert_id_in_prev_stmt().
+  */
+  thd->sys_var_tmp.ulonglong_value= 
+    thd->read_first_successful_insert_id_in_prev_stmt();
+  return (byte*) &thd->sys_var_tmp.ulonglong_value;
 }
 
 
 bool sys_var_insert_id::update(THD *thd, set_var *var)
 {
-  thd->next_insert_id= var->save_result.ulonglong_value;
+  thd->force_one_auto_inc_interval(var->save_result.ulonglong_value);
   return 0;
 }
 
@@ -2817,7 +2823,9 @@
 byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
 				   LEX_STRING *base)
 {
-  return (byte*) &thd->current_insert_id;
+  thd->sys_var_tmp.ulonglong_value= 
+    thd->auto_inc_intervals_forced.minimum();
+  return (byte*) &thd->sys_var_tmp.ulonglong_value;
 }
 
 

--- 1.63/sql/ha_federated.cc	2006-07-09 22:50:10 +02:00
+++ 1.64/sql/ha_federated.cc	2006-07-09 22:50:10 +02:00
@@ -1709,14 +1709,15 @@
   This method ensures that last_insert_id() works properly. What it simply does
   is calls last_insert_id() on the foreign database immediately after insert
   (if the table has an auto_increment field) and sets the insert id via
-  thd->insert_id(ID) (as well as storing thd->prev_insert_id)
+  thd->insert_id(ID)).
 */
 void ha_federated::update_auto_increment(void)
 {
   THD *thd= current_thd;
   DBUG_ENTER("ha_federated::update_auto_increment");
 
-  thd->insert_id(mysql->last_used_con->insert_id);
+  thd->first_successful_insert_id_in_cur_stmt= 
+    mysql->last_used_con->insert_id;
   DBUG_PRINT("info",("last_insert_id %d", stats.auto_increment_value));
 
   DBUG_VOID_RETURN;
Thread
bk commit into 5.1 tree (guilhem:1.2249)guilhem9 Jul