MySQL calls
handler::external_lock()
for every table it is going to use at the beginning of every
statement. Thus, if a table is touched for the first time, it
implicitly starts a transaction.
Note that because of pre-locking, all tables that can be
potentially used between the beginning and the end of a
statement are locked before the statement execution begins and
handler::external_lock()
is called for
all these tables. That is, if an INSERT
fires a trigger, which calls a stored procedure, that invokes
a stored function, and so forth, all tables used in the
trigger, stored procedure, function, etc., are locked in the
beginning of the INSERT
. Additionally, if
there's a construct like
IF
.. use one table
ELSE
.. use another table
both tables will be locked.
Also, if a user calls LOCK TABLES
, MySQL
will call handler::external_lock
only
once. In this case, MySQL will call
handler::start_stmt()
at the beginning of
the statement.
The following example shows how a storage engine can start a
transaction and take locking requests into account:
int my_handler::external_lock(THD *thd, int lock_type)
{
int error= 0;
my_txn *txn= (my_txn *) thd->ha_data[my_handler_hton.slot];
if (txn == NULL)
{
thd->ha_data[my_handler_hton.slot]= txn= new my_txn;
}
if (lock_type != F_UNLCK)
{
bool all_tx= 0;
if (txn->lock_count == 0)
{
txn->lock_count= 1;
txn->tx_isolation= thd->variables.tx_isolation;
all_tx= test(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK));
}
if (all_tx)
{
txn->tx_begin();
trans_register_ha(thd, TRUE, &my_handler_hton);
}
else
if (txn->stmt == 0)
{
txn->stmt= txn->new_savepoint();
trans_register_ha(thd, FALSE, &my_handler_hton);
}
}
else
{
if (txn->stmt != NULL)
{
/* Commit the transaction if we're in auto-commit mode */
my_handler_commit(thd, FALSE);
delete txn->stmt; // delete savepoint
txn->stmt= NULL;
}
}
return error;
}
Every storage engine must call
trans_register_ha()
every time it starts
a transaction. The trans_register_ha()
function registers a transaction with the MySQL server to
allow for future COMMIT
and
ROLLBACK
calls.
An additional example of implementing
external_lock()
can be found in
ha_innodb.cc
.