31
32
#include "drizzled/sql_base.h"
32
33
#include "drizzled/replication_services.h"
33
34
#include "drizzled/transaction_services.h"
35
#include "drizzled/transaction_context.h"
36
#include "drizzled/resource_context.h"
34
37
#include "drizzled/lock.h"
35
38
#include "drizzled/item/int.h"
36
39
#include "drizzled/item/empty_string.h"
37
40
#include "drizzled/field/timestamp.h"
38
41
#include "drizzled/plugin/client.h"
42
#include "drizzled/plugin/monitored_in_transaction.h"
43
#include "drizzled/plugin/transactional_storage_engine.h"
44
#include "drizzled/plugin/xa_resource_manager.h"
39
45
#include "drizzled/internal/my_sys.h"
41
47
using namespace std;
47
Transaction handling in the server
48
==================================
50
In each client connection, MySQL maintains two transactional
52
- a statement transaction,
53
- a standard, also called normal transaction.
57
"Statement transaction" is a non-standard term that comes
58
from the times when MySQL supported BerkeleyDB storage engine.
60
First of all, it should be said that in BerkeleyDB auto-commit
61
mode auto-commits operations that are atomic to the storage
62
engine itself, such as a write of a record, and are too
63
high-granular to be atomic from the application perspective
64
(MySQL). One SQL statement could involve many BerkeleyDB
65
auto-committed operations and thus BerkeleyDB auto-commit was of
68
Secondly, instead of SQL standard savepoints, BerkeleyDB
69
provided the concept of "nested transactions". In a nutshell,
70
transactions could be arbitrarily nested, but when the parent
71
transaction was committed or aborted, all its child (nested)
72
transactions were handled committed or aborted as well.
73
Commit of a nested transaction, in turn, made its changes
74
visible, but not durable: it destroyed the nested transaction,
75
all its changes would become available to the parent and
76
currently active nested transactions of this parent.
78
So the mechanism of nested transactions was employed to
79
provide "all or nothing" guarantee of SQL statements
80
required by the standard.
81
A nested transaction would be created at start of each SQL
82
statement, and destroyed (committed or aborted) at statement
83
end. Such nested transaction was internally referred to as
84
a "statement transaction" and gave birth to the term.
86
<Historical note ends>
88
Since then a statement transaction is started for each statement
89
that accesses transactional tables or uses the binary log. If
90
the statement succeeds, the statement transaction is committed.
91
If the statement fails, the transaction is rolled back. Commits
92
of statement transactions are not durable -- each such
93
transaction is nested in the normal transaction, and if the
94
normal transaction is rolled back, the effects of all enclosed
95
statement transactions are undone as well. Technically,
96
a statement transaction can be viewed as a savepoint which is
97
maintained automatically in order to make effects of one
100
The normal transaction is started by the user and is ended
101
usually upon a user request as well. The normal transaction
102
encloses transactions of all statements issued between
103
its beginning and its end.
104
In autocommit mode, the normal transaction is equivalent
105
to the statement transaction.
107
Since MySQL supports PSEA (pluggable storage engine
108
architecture), more than one transactional engine can be
109
active at a time. Hence transactions, from the server
110
point of view, are always distributed. In particular,
111
transactional state is maintained independently for each
112
engine. In order to commit a transaction the two phase
113
commit protocol is employed.
115
Not all statements are executed in context of a transaction.
116
Administrative and status information statements do not modify
117
engine data, and thus do not start a statement transaction and
118
also have no effect on the normal transaction. Examples of such
119
statements are SHOW STATUS and RESET SLAVE.
121
Similarly DDL statements are not transactional,
122
and therefore a transaction is [almost] never started for a DDL
123
statement. The difference between a DDL statement and a purely
124
administrative statement though is that a DDL statement always
125
commits the current transaction before proceeding, if there is
128
At last, SQL statements that work with non-transactional
129
engines also have no effect on the transaction state of the
130
connection. Even though they are written to the binary log,
131
and the binary log is, overall, transactional, the writes
132
are done in "write-through" mode, directly to the binlog
133
file, followed with a OS cache sync, in other words,
134
bypassing the binlog undo log (translog).
135
They do not commit the current normal transaction.
136
A failure of a statement that uses non-transactional tables
137
would cause a rollback of the statement transaction, but
138
in case there no non-transactional tables are used,
139
no statement transaction is started.
144
The server stores its transaction-related data in
145
session->transaction. This structure has two members of type
146
Session_TRANS. These members correspond to the statement and
147
normal transactions respectively:
149
- session->transaction.stmt contains a list of engines
150
that are participating in the given statement
151
- session->transaction.all contains a list of engines that
152
have participated in any of the statement transactions started
153
within the context of the normal transaction.
154
Each element of the list contains a pointer to the storage
155
engine, engine-specific transactional data, and engine-specific
158
In autocommit mode session->transaction.all is empty.
159
Instead, data of session->transaction.stmt is
160
used to commit/rollback the normal transaction.
162
The list of registered engines has a few important properties:
163
- no engine is registered in the list twice
164
- engines are present in the list a reverse temporal order --
165
new participants are always added to the beginning of the list.
167
Transaction life cycle
168
----------------------
170
When a new connection is established, session->transaction
171
members are initialized to an empty state.
172
If a statement uses any tables, all affected engines
173
are registered in the statement engine list. In
174
non-autocommit mode, the same engines are registered in
175
the normal transaction list.
176
At the end of the statement, the server issues a commit
177
or a roll back for all engines in the statement list.
178
At this point transaction flags of an engine, if any, are
179
propagated from the statement list to the list of the normal
181
When commit/rollback is finished, the statement list is
182
cleared. It will be filled in again by the next statement,
183
and emptied again at the next statement's end.
185
The normal transaction is committed in a similar way
186
(by going over all engines in session->transaction.all list)
187
but at different times:
188
- upon COMMIT SQL statement is issued by the user
189
- implicitly, by the server, at the beginning of a DDL statement
190
or SET AUTOCOMMIT={0|1} statement.
192
The normal transaction can be rolled back as well:
193
- if the user has requested so, by issuing ROLLBACK SQL
195
- if one of the storage engines requested a rollback
196
by setting session->transaction_rollback_request. This may
197
happen in case, e.g., when the transaction in the engine was
198
chosen a victim of the internal deadlock resolution algorithm
199
and rolled back internally. When such a situation happens, there
200
is little the server can do and the only option is to rollback
201
transactions in all other participating engines. In this case
202
the rollback is accompanied by an error sent to the user.
204
As follows from the use cases above, the normal transaction
205
is never committed when there is an outstanding statement
206
transaction. In most cases there is no conflict, since
207
commits of the normal transaction are issued by a stand-alone
208
administrative or DDL statement, thus no outstanding statement
209
transaction of the previous statement exists. Besides,
210
all statements that manipulate with the normal transaction
211
are prohibited in stored functions and triggers, therefore
212
no conflicting situation can occur in a sub-statement either.
213
The remaining rare cases when the server explicitly has
214
to commit the statement transaction prior to committing the normal
215
one cover error-handling scenarios (see for example
218
When committing a statement or a normal transaction, the server
219
either uses the two-phase commit protocol, or issues a commit
220
in each engine independently. The two-phase commit protocol
222
- all participating engines support two-phase commit (provide
223
plugin::StorageEngine::prepare PSEA API call) and
224
- transactions in at least two engines modify data (i.e. are
227
Note that the two phase commit is used for
228
statement transactions, even though they are not durable anyway.
229
This is done to ensure logical consistency of data in a multiple-
231
For example, imagine that some day MySQL supports unique
232
constraint checks deferred till the end of statement. In such
233
case a commit in one of the engines may yield ER_DUP_KEY,
234
and MySQL should be able to gracefully abort statement
235
transactions of other participants.
237
After the normal transaction has been committed,
238
session->transaction.all list is cleared.
240
When a connection is closed, the current normal transaction, if
243
Roles and responsibilities
244
--------------------------
246
The server has no way to know that an engine participates in
247
the statement and a transaction has been started
248
in it unless the engine says so. Thus, in order to be
249
a part of a transaction, the engine must "register" itself.
250
This is done by invoking trans_register_ha() server call.
251
Normally the engine registers itself whenever Cursor::external_lock()
252
is called. trans_register_ha() can be invoked many times: if
253
an engine is already registered, the call does nothing.
254
In case autocommit is not set, the engine must register itself
255
twice -- both in the statement list and in the normal transaction
257
In which list to register is a parameter of trans_register_ha().
259
Note, that although the registration interface in itself is
260
fairly clear, the current usage practice often leads to undesired
261
effects. E.g. since a call to trans_register_ha() in most engines
262
is embedded into implementation of Cursor::external_lock(), some
263
DDL statements start a transaction (at least from the server
264
point of view) even though they are not expected to. E.g.
265
CREATE TABLE does not start a transaction, since
266
Cursor::external_lock() is never called during CREATE TABLE. But
267
CREATE TABLE ... SELECT does, since Cursor::external_lock() is
268
called for the table that is being selected from. This has no
269
practical effects currently, but must be kept in mind
272
Once an engine is registered, the server will do the rest
275
During statement execution, whenever any of data-modifying
276
PSEA API methods is used, e.g. Cursor::write_row() or
277
Cursor::update_row(), the read-write flag is raised in the
278
statement transaction for the involved engine.
279
Currently All PSEA calls are "traced", and the data can not be
280
changed in a way other than issuing a PSEA call. Important:
281
unless this invariant is preserved the server will not know that
282
a transaction in a given engine is read-write and will not
283
involve the two-phase commit protocol!
285
At the end of a statement, server call
286
ha_autocommit_or_rollback() is invoked. This call in turn
287
invokes plugin::StorageEngine::prepare() for every involved engine.
288
Prepare is followed by a call to plugin::StorageEngine::commit_one_phase()
289
If a one-phase commit will suffice, plugin::StorageEngine::prepare() is not
290
invoked and the server only calls plugin::StorageEngine::commit_one_phase().
291
At statement commit, the statement-related read-write engine
292
flag is propagated to the corresponding flag in the normal
293
transaction. When the commit is complete, the list of registered
296
Rollback is handled in a similar fashion.
298
Additional notes on DDL and the normal transaction.
299
---------------------------------------------------
301
DDLs and operations with non-transactional engines
302
do not "register" in session->transaction lists, and thus do not
303
modify the transaction state. Besides, each DDL in
304
MySQL is prefixed with an implicit normal transaction commit
305
(a call to Session::endActiveTransaction()), and thus leaves nothing
307
However, as it has been pointed out with CREATE TABLE .. SELECT,
308
some DDL statements can start a *new* transaction.
310
Behaviour of the server in this case is currently badly
312
DDL statements use a form of "semantic" logging
313
to maintain atomicity: if CREATE TABLE .. SELECT failed,
314
the newly created table is deleted.
315
In addition, some DDL statements issue interim transaction
316
commits: e.g. ALTER Table issues a commit after data is copied
317
from the original table to the internal temporary table. Other
318
statements, e.g. CREATE TABLE ... SELECT do not always commit
320
And finally there is a group of DDL statements such as
321
RENAME/DROP Table that doesn't start a new transaction
324
This diversity makes it hard to say what will happen if
325
by chance a stored function is invoked during a DDL --
326
whether any modifications it makes will be committed or not
327
is not clear. Fortunately, SQL grammar of few DDLs allows
328
invocation of a stored function.
330
A consistent behaviour is perhaps to always commit the normal
331
transaction after all DDLs, just like the statement transaction
332
is always committed at the end of all statements.
336
Register a storage engine for a transaction.
338
Every storage engine MUST call this function when it starts
339
a transaction or a statement (that is it must be called both for the
340
"beginning of transaction" and "beginning of statement").
341
Only storage engines registered for the transaction/statement
342
will know when to commit/rollback it.
345
trans_register_ha is idempotent - storage engine may register many
346
times per transaction.
349
void TransactionServices::trans_register_ha(Session *session, bool all, plugin::StorageEngine *engine)
351
Session_TRANS *trans;
352
Ha_trx_info *ha_info;
356
trans= &session->transaction.all;
357
session->server_status|= SERVER_STATUS_IN_TRANS;
360
trans= &session->transaction.stmt;
362
ha_info= session->getEngineInfo(engine, all ? 1 : 0);
364
if (ha_info->is_started())
365
return; /* already registered, return */
367
ha_info->register_ha(trans, engine);
369
trans->no_2pc|= not engine->has_2pc();
370
if (session->transaction.xid_state.xid.is_null())
371
session->transaction.xid_state.xid.set(session->getQueryId());
57
* @defgroup Transactions
61
* Transaction handling in the server
65
* In each client connection, Drizzle maintains two transaction
66
* contexts representing the state of the:
68
* 1) Statement Transaction
69
* 2) Normal Transaction
71
* These two transaction contexts represent the transactional
72
* state of a Session's SQL and XA transactions for a single
73
* SQL statement or a series of SQL statements.
75
* When the Session's connection is in AUTOCOMMIT mode, there
76
* is no practical difference between the statement and the
77
* normal transaction, as each SQL statement is committed or
78
* rolled back depending on the success or failure of the
79
* indvidual SQL statement.
81
* When the Session's connection is NOT in AUTOCOMMIT mode, OR
82
* the Session has explicitly begun a normal SQL transaction using
83
* a BEGIN WORK/START TRANSACTION statement, then the normal
84
* transaction context tracks the aggregate transaction state of
85
* the SQL transaction's individual statements, and the SQL
86
* transaction's commit or rollback is done atomically for all of
87
* the SQL transaction's statement's data changes.
89
* Technically, a statement transaction can be viewed as a savepoint
90
* which is maintained automatically in order to make effects of one
93
* The normal transaction is started by the user and is typically
94
* ended (COMMIT or ROLLBACK) upon an explicity user request as well.
95
* The exception to this is that DDL statements implicitly COMMIT
96
* any previously active normal transaction before they begin executing.
98
* In Drizzle, unlike MySQL, plugins other than a storage engine
99
* may participate in a transaction. All plugin::TransactionalStorageEngine
100
* plugins will automatically be monitored by Drizzle's transaction
101
* manager (implemented in this source file), as will all plugins which
102
* implement plugin::XaResourceManager and register with the transaction
105
* If Drizzle's transaction manager sees that more than one resource
106
* manager (transactional storage engine or XA resource manager) has modified
107
* data state during a statement or normal transaction, the transaction
108
* manager will automatically use a two-phase commit protocol for all
109
* resources which support XA's distributed transaction protocol. Unlike
110
* MySQL, storage engines need not manually register with the transaction
111
* manager during a statement's execution. Previously, in MySQL, all
112
* handlertons would have to call trans_register_ha() at some point after
113
* modifying data state in order to have MySQL include that handler in
114
* an XA transaction. Drizzle does all of this grunt work behind the
115
* scenes for the storage engine implementers.
117
* When a connection is closed, the current normal transaction, if
118
* any is currently active, is rolled back.
120
* Transaction life cycle
121
* ----------------------
123
* When a new connection is established, session->transaction
124
* members are initialized to an empty state. If a statement uses any tables,
125
* all affected engines are registered in the statement engine list automatically
126
* in plugin::StorageEngine::startStatement() and
127
* plugin::TransactionalStorageEngine::startTransaction().
129
* You can view the lifetime of a normal transaction in the following
132
* drizzled::statement::Statement::execute()
133
* drizzled::plugin::TransactionalStorageEngine::startTransaction()
134
* drizzled::TransactionServices::registerResourceForTransaction()
135
* drizzled::TransactionServices::registerResourceForStatement()
136
* drizzled::plugin::StorageEngine::startStatement()
137
* drizzled::Cursor::write_row() <-- example...could be update_row(), etc
138
* drizzled::plugin::StorageEngine::endStatement()
139
* drizzled::TransactionServices::autocommitOrRollback()
140
* drizzled::TransactionalStorageEngine::commit() <-- or ::rollback()
141
* drizzled::XaResourceManager::xaCommit() <-- or rollback()
143
* Roles and responsibilities
144
* --------------------------
146
* Beginning of SQL Statement (and Statement Transaction)
147
* ------------------------------------------------------
149
* At the start of each SQL statement, for each storage engine
150
* <strong>that is involved in the SQL statement</strong>, the kernel
151
* calls the engine's plugin::StoragEngine::startStatement() method. If the
152
* engine needs to track some data for the statement, it should use
153
* this method invocation to initialize this data. This is the
154
* beginning of what is called the "statement transaction".
156
* <strong>For transaction storage engines (those storage engines
157
* that inherit from plugin::TransactionalStorageEngine)</strong>, the
158
* kernel automatically determines if the start of the SQL statement
159
* transaction should <em>also</em> begin the normal SQL transaction.
160
* This occurs when the connection is in NOT in autocommit mode. If
161
* the kernel detects this, then the kernel automatically starts the
162
* normal transaction w/ plugin::TransactionalStorageEngine::startTransaction()
163
* method and then calls plugin::StorageEngine::startStatement()
166
* Beginning of an SQL "Normal" Transaction
167
* ----------------------------------------
169
* As noted above, a "normal SQL transaction" may be started when
170
* an SQL statement is started in a connection and the connection is
171
* NOT in AUTOCOMMIT mode. This is automatically done by the kernel.
173
* In addition, when a user executes a START TRANSACTION or
174
* BEGIN WORK statement in a connection, the kernel explicitly
175
* calls each transactional storage engine's startTransaction() method.
177
* Ending of an SQL Statement (and Statement Transaction)
178
* ------------------------------------------------------
180
* At the end of each SQL statement, for each of the aforementioned
181
* involved storage engines, the kernel calls the engine's
182
* plugin::StorageEngine::endStatement() method. If the engine
183
* has initialized or modified some internal data about the
184
* statement transaction, it should use this method to reset or destroy
185
* this data appropriately.
187
* Ending of an SQL "Normal" Transaction
188
* -------------------------------------
190
* The end of a normal transaction is either a ROLLBACK or a COMMIT,
191
* depending on the success or failure of the statement transaction(s)
194
* The end of a "normal transaction" occurs when any of the following
197
* 1) If a statement transaction has completed and AUTOCOMMIT is ON,
198
* then the normal transaction which encloses the statement
200
* 2) If a COMMIT or ROLLBACK statement occurs on the connection
201
* 3) Just before a DDL operation occurs, the kernel will implicitly
202
* commit the active normal transaction
204
* Transactions and Non-transactional Storage Engines
205
* --------------------------------------------------
207
* For non-transactional engines, this call can be safely ignored, an
208
* the kernel tracks whether a non-transactional engine has changed
209
* any data state, and warns the user appropriately if a transaction
210
* (statement or normal) is rolled back after such non-transactional
211
* data changes have been made.
213
* XA Two-phase Commit Protocol
214
* ----------------------------
216
* During statement execution, whenever any of data-modifying
217
* PSEA API methods is used, e.g. Cursor::write_row() or
218
* Cursor::update_row(), the read-write flag is raised in the
219
* statement transaction for the involved engine.
220
* Currently All PSEA calls are "traced", and the data can not be
221
* changed in a way other than issuing a PSEA call. Important:
222
* unless this invariant is preserved the server will not know that
223
* a transaction in a given engine is read-write and will not
224
* involve the two-phase commit protocol!
226
* At the end of a statement, TransactionServices::autocommitOrRollback()
227
* is invoked. This call in turn
228
* invokes plugin::XaResourceManager::xapPepare() for every involved XA
231
* Prepare is followed by a call to plugin::TransactionalStorageEngine::commit()
232
* or plugin::XaResourceManager::xaCommit() (depending on what the resource
235
* If a one-phase commit will suffice, plugin::StorageEngine::prepare() is not
236
* invoked and the server only calls plugin::StorageEngine::commit_one_phase().
237
* At statement commit, the statement-related read-write engine
238
* flag is propagated to the corresponding flag in the normal
239
* transaction. When the commit is complete, the list of registered
240
* engines is cleared.
242
* Rollback is handled in a similar fashion.
244
* Additional notes on DDL and the normal transaction.
245
* ---------------------------------------------------
247
* CREATE TABLE .. SELECT can start a *new* normal transaction
248
* because of the fact that SELECTs on a transactional storage
249
* engine participate in the normal SQL transaction (due to
250
* isolation level issues and consistent read views).
252
* Behaviour of the server in this case is currently badly
255
* DDL statements use a form of "semantic" logging
256
* to maintain atomicity: if CREATE TABLE .. SELECT failed,
257
* the newly created table is deleted.
259
* In addition, some DDL statements issue interim transaction
260
* commits: e.g. ALTER TABLE issues a COMMIT after data is copied
261
* from the original table to the internal temporary table. Other
262
* statements, e.g. CREATE TABLE ... SELECT do not always commit
265
* And finally there is a group of DDL statements such as
266
* RENAME/DROP TABLE that doesn't start a new transaction
267
* and doesn't commit.
269
* A consistent behaviour is perhaps to always commit the normal
270
* transaction after all DDLs, just like the statement transaction
271
* is always committed at the end of all statements.
273
void TransactionServices::registerResourceForStatement(Session *session,
274
plugin::MonitoredInTransaction *monitored,
275
plugin::TransactionalStorageEngine *engine)
277
if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
280
* Now we automatically register this resource manager for the
281
* normal transaction. This is fine because a statement
282
* transaction registration should always enlist the resource
283
* in the normal transaction which contains the statement
286
registerResourceForTransaction(session, monitored, engine);
289
TransactionContext *trans= &session->transaction.stmt;
290
ResourceContext *resource_context= session->getResourceContext(monitored, 0);
292
if (resource_context->isStarted())
293
return; /* already registered, return */
295
assert(monitored->participatesInSqlTransaction());
296
assert(not monitored->participatesInXaTransaction());
298
resource_context->setMonitored(monitored);
299
resource_context->setTransactionalStorageEngine(engine);
300
trans->registerResource(resource_context);
302
trans->no_2pc|= true;
305
void TransactionServices::registerResourceForStatement(Session *session,
306
plugin::MonitoredInTransaction *monitored,
307
plugin::TransactionalStorageEngine *engine,
308
plugin::XaResourceManager *resource_manager)
310
if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
313
* Now we automatically register this resource manager for the
314
* normal transaction. This is fine because a statement
315
* transaction registration should always enlist the resource
316
* in the normal transaction which contains the statement
319
registerResourceForTransaction(session, monitored, engine, resource_manager);
322
TransactionContext *trans= &session->transaction.stmt;
323
ResourceContext *resource_context= session->getResourceContext(monitored, 0);
325
if (resource_context->isStarted())
326
return; /* already registered, return */
328
assert(monitored->participatesInXaTransaction());
329
assert(monitored->participatesInSqlTransaction());
331
resource_context->setMonitored(monitored);
332
resource_context->setTransactionalStorageEngine(engine);
333
resource_context->setXaResourceManager(resource_manager);
334
trans->registerResource(resource_context);
336
trans->no_2pc|= false;
339
void TransactionServices::registerResourceForTransaction(Session *session,
340
plugin::MonitoredInTransaction *monitored,
341
plugin::TransactionalStorageEngine *engine)
343
TransactionContext *trans= &session->transaction.all;
344
ResourceContext *resource_context= session->getResourceContext(monitored, 1);
346
if (resource_context->isStarted())
347
return; /* already registered, return */
349
session->server_status|= SERVER_STATUS_IN_TRANS;
352
trans->registerResource(resource_context);
354
assert(monitored->participatesInSqlTransaction());
355
assert(not monitored->participatesInXaTransaction());
357
resource_context->setMonitored(monitored);
358
resource_context->setTransactionalStorageEngine(engine);
359
trans->no_2pc|= true;
361
if (session->transaction.xid_state.xid.is_null())
362
session->transaction.xid_state.xid.set(session->getQueryId());
364
/* Only true if user is executing a BEGIN WORK/START TRANSACTION */
365
if (! session->getResourceContext(monitored, 0)->isStarted())
366
registerResourceForStatement(session, monitored, engine);
369
void TransactionServices::registerResourceForTransaction(Session *session,
370
plugin::MonitoredInTransaction *monitored,
371
plugin::TransactionalStorageEngine *engine,
372
plugin::XaResourceManager *resource_manager)
374
TransactionContext *trans= &session->transaction.all;
375
ResourceContext *resource_context= session->getResourceContext(monitored, 1);
377
if (resource_context->isStarted())
378
return; /* already registered, return */
380
session->server_status|= SERVER_STATUS_IN_TRANS;
382
trans->registerResource(resource_context);
384
assert(monitored->participatesInSqlTransaction());
386
resource_context->setMonitored(monitored);
387
resource_context->setXaResourceManager(resource_manager);
388
resource_context->setTransactionalStorageEngine(engine);
389
trans->no_2pc|= true;
391
if (session->transaction.xid_state.xid.is_null())
392
session->transaction.xid_state.xid.set(session->getQueryId());
394
/* Only true if user is executing a BEGIN WORK/START TRANSACTION */
395
if (! session->getResourceContext(monitored, 0)->isStarted())
396
registerResourceForStatement(session, monitored, engine, resource_manager);