| Bug #21726 | Incorrect result with multiple invocations of LAST_INSERT_ID | ||
|---|---|---|---|
| Submitted: | 18 Aug 2006 19:57 | Modified: | 13 Nov 2006 18:28 | 
| Reporter: | Dean Ellis | Email Updates: | |
| Status: | Closed | Impact on me: | |
| Category: | MySQL Server: Stored Routines | Severity: | S1 (Critical) | 
| Version: | 5.0.25-bk,4.1 | OS: | |
| Assigned to: | Tomash Brechko | CPU Architecture: | Any | 
   [18 Aug 2006 19:57]
   Dean Ellis        
  
 
   [31 Aug 2006 5:32]
   Valeriy Kravchuk        
  Bug #21808 was marked as a duplicate of this one. 5.0.24, Windows.
   [31 Aug 2006 8:01]
   Guilhem Bichot        
  It will be interesting to check if this bug also exists in 5.1: I changed some things in LAST_INSERT_ID() in 5.1 in July.
   [5 Sep 2006 19:48]
   Vance Rodriguez        
  This bug may have/be a duplicate at Bug #21616.
   [14 Sep 2006 19:44]
   Sveta Smirnova        
  There is duplicate bug #22327
   [22 Sep 2006 12:34]
   Bugs System        
  A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/12394 ChangeSet@1.2264, 2006-09-22 16:33:20+04:00, kroki@moonlight.intranet +10 -0 BUG#21726: Incorrect result with multiple invocations of LAST_INSERT_ID Non-upper-level INSERTs (the ones in the body of stored procedure, stored function, or trigger) into a table that have AUTO_INCREMENT column didn't affected the result of LAST_INSERT_ID() on this level. The problem was introduced with the fix of bug 6880, where current insert_id value that was remembered on the first call to LAST_INSERT_ID() was returned from that function until it was reset before the next _upper-level_ statement. The very procedure of remembering current insert_id had another defects that made the fix for bug 6880 only a partial solution. The fix for bug#21726 employs the following protocol: - remember insert_id value after the statement execution (which is at that point equals to the first insert_id value generated by the statement or the result of its explicit setting). - on statements "LAST_INSERT_ID(expr)", "SET LAST_INSERT_ID= expr", and on binary log command "LAST_INSERT_ID= constant" update remembered insert_id value. Thus, the value returned by LAST_INSERT_ID() is not affected by values generated by current statement, but affected by its explicit update. This fix is for 5.0 only, as in 5.1 WL 3146 solves the same problems in a more generic way. This patch tries to produce the same results as 5.1.
   [27 Sep 2006 16:14]
   Konstantin Osipov        
  I'm the second reviewer here, after Rafal. Setting a ETA.
   [28 Sep 2006 19:05]
   Bugs System        
  A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/12762 ChangeSet@1.2289, 2006-09-28 23:04:11+04:00, kroki@moonlight.intranet +13 -0 BUG#21726: Incorrect result with multiple invocations of LAST_INSERT_ID Non-upper-level INSERTs (the ones in the body of stored procedure, stored function, or trigger) into a table that have AUTO_INCREMENT column didn't affected the result of LAST_INSERT_ID() on this level. The problem was introduced with the fix of bug 6880, which in turn was introduced with the fix of bug 3117, where current insert_id value was remembered on the first call to LAST_INSERT_ID() (bug 3117) and was returned from that function until it was reset before the next _upper-level_ statement (bug 6880). The fix for bug#21726 brings back the behaviour of version 4.0, and implements the following: remember insert_id value in Item_func_last_insert_id::fix_fields() (which at that point equals to the first insert_id value generated by the previous statement), and return that remembered value from THD::insert_id(). Thus, the value returned by LAST_INSERT_ID() is not affected by values generated by current statement, nor by LAST_INSERT_ID(expr) calls in this statement. Version 5.1 does not have this bug (it was fixed by WL 3146).
   [2 Oct 2006 10:29]
   Bugs System        
  A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/12930 ChangeSet@1.2293, 2006-10-02 14:28:23+04:00, kroki@moonlight.intranet +14 -0 BUG#21726: Incorrect result with multiple invocations of LAST_INSERT_ID Non-upper-level INSERTs (the ones in the body of stored procedure, stored function, or trigger) into a table that have AUTO_INCREMENT column didn't affected the result of LAST_INSERT_ID() on this level. The problem was introduced with the fix of bug 6880, which in turn was introduced with the fix of bug 3117, where current insert_id value was remembered on the first call to LAST_INSERT_ID() (bug 3117) and was returned from that function until it was reset before the next _upper-level_ statement (bug 6880). The fix for bug#21726 brings back the behaviour of version 4.0, and implements the following: remember insert_id value at the beginning of the statement or expression (which at that point equals to the first insert_id value generated by the previous statement), and return that remembered value from LAST_INSERT_ID() or @@LAST_INSERT_ID. Thus, the value returned by LAST_INSERT_ID() is not affected by values generated by current statement, nor by LAST_INSERT_ID(expr) calls in this statement. Version 5.1 does not have this bug (it was fixed by WL 3146).
   [2 Oct 2006 13:45]
   Tomash Brechko        
  Pushed to 5.0.
   [2 Oct 2006 13:50]
   Tomash Brechko        
  Pushed to 5.0.26, that is.
   [2 Oct 2006 15:04]
   Konstantin Osipov        
  For the record: the patch was approved over email, phone, irc. Me, Rafal and Guilhem all looked at the final patch.
   [3 Oct 2006 9:39]
   Bugs System        
  A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/12990 ChangeSet@1.2294, 2006-10-03 13:38:16+04:00, kroki@moonlight.intranet +9 -0 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.
   [3 Oct 2006 20:12]
   Russ Brown        
  Is there a workaround that I can use to get around this problem while I wait for an official release which contains the fix? The only one I can think of is to wrap the inserts into individual functions, but that seems really messy and over the top... Thanks.
   [4 Oct 2006 19:12]
   Paul DuBois        
  Noted in 5.0.26. The value of LAST_INSERT_ID() was not always updated correctly within stored routines.
   [5 Oct 2006 9:54]
   Tomash Brechko        
  Setting back to 'In progress' for 4.1.
   [6 Oct 2006 9:35]
   Bugs System        
  A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/13222 ChangeSet@1.2554, 2006-10-06 13:34:07+04:00, kroki@moonlight.intranet +13 -0 BUG#21726: Incorrect result with multiple invocations of LAST_INSERT_ID. Note: bug#21726 does not directly apply to 4.1, as it doesn't have stored procedures. However, 4.1 had some bugs that were fixed in 5.0 by the patch for bug#21726, and this patch is a backport of those fixes. Namely, in 4.1 it fixes: - LAST_INSERT_ID(expr) didn't return value of expr (4.1 specific). - LAST_INSERT_ID() could return the value generated by current statement if the call happens after the generation, like in CREATE TABLE t1 (i INT AUTO_INCREMENT PRIMARY KEY, j INT); INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); - Redundant binary log LAST_INSERT_ID_EVENTs could be generated.
   [11 Oct 2006 12:20]
   Paul van Rossem        
  Bug still present in 5.0.26.
   [11 Oct 2006 12:28]
   Tomash Brechko        
  Paul, could you please provide an example the reveals this bug in 5.0.26?
   [12 Oct 2006 15:46]
   Peter Andrews        
  This bug is stil present in 5.1.11 beta windows. Here is a sample that returns increasing values for last_insert_id() despite the table remaining with only one row: DROP TABLE IF EXISTS `d`; CREATE TABLE `d` ( `k` int(11) NOT NULL auto_increment, `a` int(11) default NULL, `c` int(11) default NULL, PRIMARY KEY (`k`), UNIQUE KEY `idx_1` (`a`) ) ENGINE=InnoDB; insert into d values(null,4,2); insert into d values(null,5,2),(null,4,2); select last_insert_id(); select * from d; insert into d values(null,5,2),(null,4,2); select last_insert_id(); select * from d; insert into d values(null,5,2),(null,4,2); select last_insert_id(); select * from d; insert into d values(null,5,2),(null,4,2); select last_insert_id(); select * from d; insert into d values(null,5,2),(null,4,2); select last_insert_id(); select * from d;
   [13 Oct 2006 7:22]
   Paul van Rossem        
  Example where this still fails in 5.0.26:
CREATE TABLE T3
  (T3ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE T1
  (T1ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, 
   T3Ref INTEGER,
   FOREIGN KEY (T3Ref) REFERENCES T3(T3ID) ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE T2
  (T2ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
   T1Ref INTEGER NOT NULL,
   FOREIGN KEY (T1Ref) REFERENCES T1(T1ID) ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO T3(T3ID) VALUES (123);
INSERT INTO T1(T1ID, T3Ref) VALUES (33, 123);
INSERT INTO T2(T2ID, T1Ref) VALUES (222, 33);
CREATE PROCEDURE T1_COPY (srcT1 INTEGER)
BEGIN
  DECLARE dstT1 INTEGER
  DECLARE srcT3 INTEGER; DECLARE dstT3 INTEGER;
  DECLARE srcT2 INTEGER; DECLARE dstT2 INTEGER;
  DECLARE done SMALLINT DEFAULT 0;
  DECLARE t2Cursor CURSOR FOR SELECT T2ID FROM T2 WHERE T1Ref = srcT1;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  SELECT T3Ref INTO srcT3 FROM T1 WHERE T1ID = srcT1;
  CALL T3_COPY(srcT3, dstT3);
  INSERT INTO T1(T3Ref) VALUES (dstT3);
  SET dstT1 = LAST_INSERT_ID();  /* this sets the value of the last T3 insert,              
                                     not of the T1 insert! */
  OPEN t2Cursor;
  REPEAT
    FETCH t2Cursor INTO srcT2;
    IF NOT done THEN 
       CALL T2_COPY(srcT2, dstT1, dstT2); /* fails because of invalid dstT1 */
    END IF;
  UNTIL done END REPEAT;
END $
CREATE PROCEDURE T2_COPY (srcT2 INTEGER, dstT1 INTEGER, OUT newT2 INTEGER)
BEGIN
  DECLARE srcT1 INTEGER;
  SELECT T1Ref INTO srcT1 FROM T2 WHERE T2ID = srcT2;
  INSERT INTO T2(T1Ref) VALUES (dstT1);
  SET newT2 = LAST_INSERT_ID();
END $
CREATE PROCEDURE T3_COPY (srcT3 INTEGER, OUT newT3 INTEGER) 
BEGIN
  INSERT INTO T3() VALUES ();
  SET newT3 = LAST_INSERT_ID();
END $
CALL T1_COPY(33, result);  /* fails for resons mentioned above */
 
   [13 Oct 2006 7:59]
   Tomash Brechko        
  Paul, after running your test case I get the following correct results: SELECT * FROM T1; #+------+-------+ #| T1ID | T3Ref | #+------+-------+ #| 33 | 123 | #| 34 | 124 | <= '34' was generated by INSERT INTO T1(T3Ref) VALUES (dstT3); #+------+-------+ and it was assigned to dstT1. SELECT * FROM T2; #+------+-------+ #| T2ID | T1Ref | #+------+-------+ #| 222 | 33 | #| 223 | 34 | <= '34' was inserted by INSERT INTO T2(T1Ref) VALUES (dstT1); #+------+-------+ which is correct. SELECT * FROM T3; #+------+ #| T3ID | #+------+ #| 123 | #| 124 | #+------+ If you results differ from the above, please paste them, and also the output of "SELECT VERSION();". If you have the same results, then I guess everything is correct.
   [13 Oct 2006 9:08]
   Tomash Brechko        
  Peter, I have opened a new bug#23249 based on your input, thanks.
   [13 Oct 2006 11:24]
   Paul van Rossem        
  Tomash, I re-downloaded and re-installed windows server 5.0.26, and now I get the correct results. So the problem must have been with my server installation. Owe you an apology. The problem appears solved with 5.0.26. Regards, Paul.
   [13 Oct 2006 11:32]
   Tomash Brechko        
  Paul, that's great. Thanks for letting us know.
   [16 Oct 2006 16:47]
   Konstantin Osipov        
  Approved the version for 4.1
   [17 Oct 2006 10:01]
   Tomash Brechko        
  4.1 version queued to 4.1-runtime and null-merged to 5.0-runtime and 5.1-runtime.
   [3 Nov 2006 10:54]
   Tomash Brechko        
  Bug#23932 relates to this bug.
   [11 Nov 2006 3:27]
   Paul DuBois        
  Noted in 4.1.22 changelog. The value of LAST_INSERT_ID() was not always updated correctly within stored routines.
   [13 Nov 2006 18:28]
   Paul DuBois        
  Corrected the 4.1.21 changelog entry: Redundant binary log LAST_INSERT_ID events could be generated; LAST_INSERT_ID(expr) didn't return value of expr; LAST_INSERT_ID() could return the value generated by the current statement if the call happens after value generation, as in: CREATE TABLE t1 (i INT AUTO_INCREMENT PRIMARY KEY, j INT); INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID());
