Bug #8650 | InnoDB does not rollback SQL statement at an error | ||
---|---|---|---|
Submitted: | 21 Feb 2005 15:26 | Modified: | 18 Apr 2005 17:49 |
Reporter: | Matthias Leich | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S1 (Critical) |
Version: | 5.0.3-pre | OS: | |
Assigned to: | Heikki Tuuri | CPU Architecture: | Any |
[21 Feb 2005 15:26]
Matthias Leich
[21 Feb 2005 15:27]
Matthias Leich
test case
Attachment: ml27.test (application/test, text), 2.56 KiB.
[21 Feb 2005 16:31]
Aleksey Kishkin
a note: in order to see output of select, I run mysql-test-run --record ml27 and then look at ml27.result
[27 Feb 2005 13:01]
Sergei Golubchik
the reason is that ha_innobase::external lock does not call trans_register_ha(thd, FALSE, &innobase_hton); for every statement in a transaction. Being not registered InnoDB does not receive a rollback event.
[17 Mar 2005 18:30]
Heikki Tuuri
Sergei, does InnoDB need to register every start of a statement? I thought that the function only needs to be called to register a new TRANSACTION. Regards, Heikki /************************************************************************* Registers the InnoDB transaction in MySQL, to receive commit/rollback events. This function must be called every time InnoDB starts a transaction internally. */ static void register_trans( /*===========*/ THD* thd) /* in: thd to use the handle */ { /* Register the start of the statement */ trans_register_ha(thd, FALSE, &innobase_hton); if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { /* No autocommit mode, register for a transaction */ trans_register_ha(thd, TRUE, &innobase_hton); } }
[17 Mar 2005 19:05]
Sergei Golubchik
register_trans_ha(FALSE) should be called for each statement, register_trans_ha(TRUE) once per transaction. register_trans() that you quoted does both, registers for the start of transaction and the start of the statement (first statement in a tranaction), but for the rest of the transaction InnoDB still needs to call register_trans_ha(FALSE).
[17 Mar 2005 21:56]
Heikki Tuuri
Hi! The function could have a more descriptive name. There could be: trans_register_ha() and stmt_register_ha() If a new SQL statement starts a transaction, do we need to call BOTH trans_register_ha() AND stmt_register_ha()? Or is trans_register_ha() enough? Is there any harm from calling trans_register_ha() several times during one transaction? Is there any harm from calling stmt_register_ha() several times before a statement? Regards, Heikki handler.cc: DESCRIPTION Every storage engine MUST call this function when it starts a transaction or a statement (that is it must be called both for the "beginning of transaction" and "beginning of statement"). Only storage engines registered for the transaction/statement will know when to commit/rollback it. */ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) { THD_TRANS *trans; DBUG_ENTER("trans_register_ha"); DBUG_PRINT("enter",("%s", all ? "all" : "stmt")); if (all) { trans= &thd->transaction.all; thd->server_status|= SERVER_STATUS_IN_TRANS; } else trans= &thd->transaction.stmt; #ifndef DBUG_OFF handlerton **ht=trans->ht; while (*ht) { DBUG_ASSERT(*ht != ht_arg); ht++; } #endif trans->ht[trans->nht++]=ht_arg; trans->no_2pc|=(ht_arg->prepare==0); if (thd->transaction.xid.is_null()) thd->transaction.xid.set(thd->query_id); DBUG_VOID_RETURN; } ha_innodb.cc: /************************************************************************* Registers the InnoDB transaction in MySQL, to receive commit/rollback events. This function must be called every time InnoDB starts a transaction internally. */ static void register_trans( /*===========*/ THD* thd) /* in: thd to use the handle */ { /* Register the start of the statement */ trans_register_ha(thd, FALSE, &innobase_hton); if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { /* No autocommit mode, register for a transaction */ trans_register_ha(thd, TRUE, &innobase_hton); } }
[17 Mar 2005 22:07]
Heikki Tuuri
Hi! The current code apparently only calls stmt_register_ha() if AUTOCOMMIT=1. But still MySQL commits the WHOLE transaction at the statement end, not just the statement. Is the rule that in the AUTOCOMMIT=1 mode we should not register the transaction, but register just the statement? How about the AUTOCOMMIT=0 mode? It would help a lot if the concepts 'transaction' and 'statement' were separated in handler.cc. A 'transaction' would consist of one or more 'statements'. Regards, Heikki
[18 Mar 2005 0:07]
Heikki Tuuri
Hi! Looks like the following code fixes the problems. I still have to think about the of the 5 other places where a transaction can start: The flag trx->active_trans is set to 1 in 1. ::external_lock(), 2. ::start_stmt(), 3. innobase_query_caching_of_table_permitted(), 4. innobase_savepoint(), 5. ::init_table_handle_for_HANDLER(), 6. innobase_start_trx_and_assign_read_view(), 7. ::transactional_table_lock() Maybe the easiest way to avoid multiple declarations of the same handler for the same statement is to check in ha_register_trans() in handler.cc whether the handler already is declared. Regards, Heikki ::start_stmt() } /* Set the MySQL flag to mark that there is an active transaction */ if (trx->active_trans == 0) { register_trans(thd); trx->active_trans = 1; } else { /* Register the statement started: HOW DO WE KNOW if we have registered the statement already? */ trans_register_ha(thd, FALSE, &innobase_hton); } return(0); } ::external_lock() if (lock_type != F_UNLCK) { /* MySQL is setting a new table lock */ /* Set the MySQL flag to mark that there is an active transaction */ if (trx->active_trans == 0) { register_trans(thd); trx->active_trans = 1; } else if (trx->n_mysql_tables_in_use == 0) { /* Register the statement started */ trans_register_ha(thd, FALSE, &innobase_hton); }
[18 Mar 2005 8:09]
Jan Lindström
It would make sense if indeed the function has a more descriptive name. There really should be: trans_register_ha() to register transaction i.e. this is called once per transaction. and stmt_register_ha() to register start of SQL statement i.e. this is called once per statement. Regards JanL
[5 Apr 2005 15:44]
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/internals/23676
[18 Apr 2005 17:49]
Heikki Tuuri
Fixed in 5.0.4