6.5 Roles and Responsibilities

The server has only one way to know that an engine participates in
the statement and a transaction has been started in an engine: the
engine says so. So, in order to be a part of a transaction, an
engine must "register" itself. This is done by invoking the
trans_register_ha() server call. Normally the engine registers
itself whenever handler::external_lock() is called. Although
trans_register_ha() can be invoked many times, it does nothing if
the engine is already registered. If autocommit is not set, the
engine must register itself twice -- both in the statement list
and in the normal transaction list. A parameter of
trans_register_ha() specifies which list to register.

Note: Although the registration interface in itself is fairly
clear, the current usage practice often leads to undesired
effects. For example, since a call to trans_register_ha() in most
engines is embedded into an implementation of
handler::external_lock(), some DDL statements start a transaction
(at least from the server point of view) even though they are not
expected to. For example CREATE TABLE does not start a
transaction, since handler::external_lock() is never called during
CREATE TABLE. But CREATE TABLE ... SELECT does, since
handler::external_lock() is called for the table that is being
selected from. This has no practical effects currently, but we
must keep it in mind nevertheless.

Once an engine is registered, the server will do the rest of the
work.

During statement execution, whenever any data-modifying PSEA API
methods are used (for example, handler::write_row() or
handler::update_row()), the read-write flag is raised in the
statement transaction for the relevant engine. Currently All PSEA
calls are "traced", and the only way to change data is to issue a
PSEA call. Important: Unless this invariant is preserved, the
server will not know that a transaction in a given engine is
read-write and will not involve the two-phase commit protocol!

The end of a statement causes invocation of the
ha_autocommit_or_rollback() server call, which in turn invokes
handlerton::prepare() for every involved engine. After
handlerton::prepare(), there's a call to
handlerton::commit_one_phase(). If a one-phase commit will
suffice, handlerton::prepare() is not invoked and the server only
calls handlerton::commit_one_phase(). At statement commit, the
statement-related read-write engine flag is propagated to the
corresponding flag in the normal transaction. When the commit is
complete, the list of registered engines is cleared.