Merge lp:~jaypipes/drizzle/explicit-transaction into lp:~drizzle-trunk/drizzle/development

Proposed by Jay Pipes
Status: Merged
Merged at revision: not available
Proposed branch: lp:~jaypipes/drizzle/explicit-transaction
Merge into: lp:~drizzle-trunk/drizzle/development
Prerequisite: lp:~jaypipes/drizzle/explicit-statement
Diff against target: 807 lines (+337/-162)
9 files modified
drizzled/plugin/transactional_storage_engine.cc (+30/-6)
drizzled/plugin/transactional_storage_engine.h (+34/-3)
drizzled/session.cc (+2/-5)
drizzled/session.h (+17/-9)
drizzled/transaction_services.cc (+111/-81)
drizzled/transaction_services.h (+27/-2)
plugin/innobase/handler/ha_innodb.cc (+17/-56)
tests/r/transaction.result (+43/-0)
tests/t/transaction.test (+56/-0)
To merge this branch: bzr merge lp:~jaypipes/drizzle/explicit-transaction
Reviewer Review Type Date Requested Status
Brian Aker Pending
Drizzle Developers Pending
Review via email: mp+20102@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jay Pipes (jaypipes) wrote :

    Completes the work of removing the weirdness around transaction
    boundaries in the storage engine API.

    * Transactional storage engines are now all explicitly notified
      of the start of a new "normal" transaction in the new PSE API
      method plugin::TransactionalStorageEngine::doStartTransaction()
      This new method takes a start_transaction_option_t as one of its
      parameters, and passing this option allows the storage engine
      API to cleanly signal the start of a consistent snapshot (and in
      the future additional transaction attributes). This meant the
      removal of the old start_consistent_snapshot() method.

    * The TransactionServices component now fully manages the transaction
      boundaries, notification of transaction boundaries to participating
      resource managers (transactional storage engines)

    Adds a simple test case (to be expanded with future XA work) for
    transaction behaviour.

Revision history for this message
Stewart Smith (stewart) wrote :

explicit transaction start++

(i should look at the code too, but love explicit)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'drizzled/plugin/transactional_storage_engine.cc'
2--- drizzled/plugin/transactional_storage_engine.cc 2010-02-14 19:27:57 +0000
3+++ drizzled/plugin/transactional_storage_engine.cc 2010-02-25 04:43:15 +0000
4@@ -2,6 +2,7 @@
5 * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
6 *
7 * Copyright (C) 2008 Sun Microsystems
8+ * Copyright (c) 2009-2010 Jay Pipes <jaypipes@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12@@ -92,12 +93,35 @@
13 return 0;
14 }
15
16-int TransactionalStorageEngine::startConsistentSnapshot(Session *session)
17-{
18- for_each(vector_of_transactional_engines.begin(), vector_of_transactional_engines.end(),
19- bind2nd(mem_fun(&TransactionalStorageEngine::doStartConsistentSnapshot),
20- session));
21- return 0;
22+struct StartTransactionFunc :public unary_function<TransactionalStorageEngine *, int>
23+{
24+ Session *session;
25+ start_transaction_option_t options;
26+ StartTransactionFunc(Session *in_session, start_transaction_option_t in_options) :
27+ session(in_session),
28+ options(in_options)
29+ {}
30+ result_type operator()(argument_type engine) const
31+ {
32+ return engine->startTransaction(session, options);
33+ }
34+};
35+
36+int TransactionalStorageEngine::notifyStartTransaction(Session *session, start_transaction_option_t options)
37+{
38+ if (vector_of_transactional_engines.empty())
39+ return 0;
40+ else
41+ {
42+ StartTransactionFunc functor(session, options);
43+ vector<int> results;
44+ results.reserve(vector_of_transactional_engines.size());
45+ transform(vector_of_transactional_engines.begin(),
46+ vector_of_transactional_engines.end(),
47+ results.begin(),
48+ functor);
49+ return *max_element(results.begin(), results.end());
50+ }
51 }
52
53 bool TransactionalStorageEngine::addPlugin(TransactionalStorageEngine *engine)
54
55=== modified file 'drizzled/plugin/transactional_storage_engine.h'
56--- drizzled/plugin/transactional_storage_engine.h 2010-02-25 04:43:14 +0000
57+++ drizzled/plugin/transactional_storage_engine.h 2010-02-25 04:43:15 +0000
58@@ -2,6 +2,7 @@
59 * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
60 *
61 * Copyright (C) 2008 Sun Microsystems
62+ * Copyright (c) 2009-2010 Jay Pipes <jaypipes@gmail.com>
63 *
64 * This program is free software; you can redistribute it and/or modify
65 * it under the terms of the GNU General Public License as published by
66@@ -20,6 +21,7 @@
67 #ifndef DRIZZLED_PLUGIN_TRANSACTIONAL_STORAGE_ENGINE_H
68 #define DRIZZLED_PLUGIN_TRANSACTIONAL_STORAGE_ENGINE_H
69
70+#include "drizzled/definitions.h" /* for start_transaction_option_t */
71 #include "drizzled/plugin/storage_engine.h"
72 #include "drizzled/transaction_services.h"
73
74@@ -63,6 +65,13 @@
75
76 virtual ~TransactionalStorageEngine();
77
78+ int startTransaction(Session *session, start_transaction_option_t options)
79+ {
80+ TransactionServices &transaction_services= TransactionServices::singleton();
81+ transaction_services.registerResourceForTransaction(session, this);
82+ return doStartTransaction(session, options);
83+ }
84+
85 void startStatement(Session *session)
86 {
87 TransactionServices &transaction_services= TransactionServices::singleton();
88@@ -103,14 +112,12 @@
89 /**
90 * The below static class methods wrap the interaction
91 * of the vector of transactional storage engines.
92- *
93- * @todo kill these. they belong in TransactionServices.
94 */
95+ static int notifyStartTransaction(Session *session, start_transaction_option_t options);
96 /**
97 * @todo Kill this one entirely. It's implementation, not interface...
98 */
99 static int releaseTemporaryLatches(Session *session);
100- static int startConsistentSnapshot(Session *session);
101
102 /* Class Methods for operating on plugin */
103 static bool addPlugin(plugin::TransactionalStorageEngine *engine);
104@@ -121,6 +128,30 @@
105
106 /*
107 * Indicates to a storage engine the start of a
108+ * new SQL transaction. This is called ONLY in the following
109+ * scenarios:
110+ *
111+ * 1) An explicit BEGIN WORK/START TRANSACTION is called
112+ * 2) After an explicit COMMIT AND CHAIN is called
113+ * 3) After an explicit ROLLBACK AND RELEASE is called
114+ * 4) When in AUTOCOMMIT mode and directly before a new
115+ * SQL statement is started.
116+ *
117+ * Engines should typically use the doStartStatement()
118+ * and doEndStatement() methods to manage transaction state,
119+ * since the kernel ALWAYS notifies engines at the start
120+ * and end of statement transactions and at the end of the
121+ * normal transaction by calling doCommit() or doRollback().
122+ */
123+ virtual int doStartTransaction(Session *session, start_transaction_option_t options)
124+ {
125+ (void) session;
126+ (void) options;
127+ return 0;
128+ }
129+
130+ /*
131+ * Indicates to a storage engine the start of a
132 * new SQL statement.
133 */
134 virtual void doStartStatement(Session *session)
135
136=== modified file 'drizzled/session.cc'
137--- drizzled/session.cc 2010-02-22 16:14:47 +0000
138+++ drizzled/session.cc 2010-02-25 04:43:15 +0000
139@@ -849,12 +849,9 @@
140 options|= OPTION_BEGIN;
141 server_status|= SERVER_STATUS_IN_TRANS;
142
143- if (opt == START_TRANS_OPT_WITH_CONS_SNAPSHOT)
144+ if (plugin::TransactionalStorageEngine::notifyStartTransaction(this, opt))
145 {
146- if (plugin::TransactionalStorageEngine::startConsistentSnapshot(this))
147- {
148- result= false;
149- }
150+ result= false;
151 }
152 }
153
154
155=== modified file 'drizzled/session.h'
156--- drizzled/session.h 2010-02-22 16:14:47 +0000
157+++ drizzled/session.h 2010-02-25 04:43:15 +0000
158@@ -312,15 +312,23 @@
159 */
160 void *ha_ptr;
161 /**
162- 0: Life time: one statement within a transaction. If @@autocommit is
163- on, also represents the entire transaction.
164- @sa trans_register_ha()
165-
166- 1: Life time: one transaction within a connection.
167- If the storage engine does not participate in a transaction,
168- this should not be used.
169- @sa trans_register_ha()
170- */
171+ * Resource contexts for both the "statement" and "normal"
172+ * transactions.
173+ *
174+ * Resource context at index 0:
175+ *
176+ * Life time: one statement within a transaction. If @@autocommit is
177+ * on, also represents the entire transaction.
178+ *
179+ * Resource context at index 1:
180+ *
181+ * Life time: one transaction within a connection.
182+ *
183+ * @note
184+ *
185+ * If the storage engine does not participate in a transaction,
186+ * there will not be a resource context.
187+ */
188 drizzled::ResourceContext resource_context[2];
189
190 Ha_data() :ha_ptr(NULL) {}
191
192=== modified file 'drizzled/transaction_services.cc'
193--- drizzled/transaction_services.cc 2010-02-25 04:43:14 +0000
194+++ drizzled/transaction_services.cc 2010-02-25 04:43:15 +0000
195@@ -2,6 +2,7 @@
196 * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
197 *
198 * Copyright (C) 2008 Sun Microsystems
199+ * Copyright (c) Jay Pipes <jaypipes@gmail.com>
200 *
201 * This program is free software; you can redistribute it and/or modify
202 * it under the terms of the GNU General Public License as published by
203@@ -245,39 +246,80 @@
204 session->transaction.all list is cleared.
205
206 When a connection is closed, the current normal transaction, if
207- any, is rolled back.
208+ any is currently active, is rolled back.
209
210 Roles and responsibilities
211 --------------------------
212
213- The server has no way to know that an engine participates in
214- the statement and a transaction has been started
215- in it unless the engine says so. Thus, in order to be
216- a part of a transaction, the engine must "register" itself.
217- This is done by invoking trans_register_ha() server call.
218- Normally the engine registers itself whenever Cursor::external_lock()
219- is called. trans_register_ha() can be invoked many times: if
220- an engine is already registered, the call does nothing.
221- In case autocommit is not set, the engine must register itself
222- twice -- both in the statement list and in the normal transaction
223- list.
224- In which list to register is a parameter of trans_register_ha().
225-
226- Note, that although the registration interface in itself is
227- fairly clear, the current usage practice often leads to undesired
228- effects. E.g. since a call to trans_register_ha() in most engines
229- is embedded into implementation of Cursor::external_lock(), some
230- DDL statements start a transaction (at least from the server
231- point of view) even though they are not expected to. E.g.
232- CREATE TABLE does not start a transaction, since
233- Cursor::external_lock() is never called during CREATE TABLE. But
234- CREATE TABLE ... SELECT does, since Cursor::external_lock() is
235- called for the table that is being selected from. This has no
236- practical effects currently, but must be kept in mind
237- nevertheless.
238-
239- Once an engine is registered, the server will do the rest
240- of the work.
241+ Beginning of SQL Statement (and Statement Transaction)
242+ ------------------------------------------------------
243+
244+ At the start of each SQL statement, for each storage engine
245+ <strong>that is involved in the SQL statement</strong>, the kernel
246+ calls the engine's plugin::StoragEngine::startStatement() method. If the
247+ engine needs to track some data for the statement, it should use
248+ this method invocation to initialize this data. This is the
249+ beginning of what is called the "statement transaction".
250+
251+ <strong>For transaction storage engines (those storage engines
252+ that inherit from plugin::TransactionalStorageEngine)</strong>, the
253+ kernel automatically determines if the start of the SQL statement
254+ transaction should <em>also</em> begin the normal SQL transaction.
255+ This occurs when the connection is in NOT in autocommit mode. If
256+ the kernel detects this, then the kernel automatically starts the
257+ normal transaction w/ plugin::TransactionalStorageEngine::startTransaction()
258+ method and then calls plugin::StorageEngine::startStatement()
259+ afterwards.
260+
261+ Beginning of an SQL "Normal" Transaction
262+ ----------------------------------------
263+
264+ As noted above, a "normal SQL transaction" may be started when
265+ an SQL statement is started in a connection and the connection is
266+ NOT in AUTOCOMMIT mode. This is automatically done by the kernel.
267+
268+ In addition, when a user executes a START TRANSACTION or
269+ BEGIN WORK statement in a connection, the kernel explicitly
270+ calls each transactional storage engine's startTransaction() method.
271+
272+ Ending of an SQL Statement (and Statement Transaction)
273+ ------------------------------------------------------
274+
275+ At the end of each SQL statement, for each of the aforementioned
276+ involved storage engines, the kernel calls the engine's
277+ plugin::StorageEngine::endStatement() method. If the engine
278+ has initialized or modified some internal data about the
279+ statement transaction, it should use this method to reset or destroy
280+ this data appropriately.
281+
282+ Ending of an SQL "Normal" Transaction
283+ -------------------------------------
284+
285+ The end of a normal transaction is either a ROLLBACK or a COMMIT,
286+ depending on the success or failure of the statement transaction(s)
287+ it encloses.
288+
289+ The end of a "normal transaction" occurs when any of the following
290+ occurs:
291+
292+ 1) If a statement transaction has completed and AUTOCOMMIT is ON,
293+ then the normal transaction which encloses the statement
294+ transaction ends
295+ 2) If a COMMIT or ROLLBACK statement occurs on the connection
296+ 3) Just before a DDL operation occurs, the kernel will implicitly
297+ commit the active normal transaction
298+
299+ Transactions and Non-transactional Storage Engines
300+ --------------------------------------------------
301+
302+ For non-transactional engines, this call can be safely ignored, and
303+ the kernel tracks whether a non-transactional engine has changed
304+ any data state, and warns the user appropriately if a transaction
305+ (statement or normal) is rolled back after such non-transactional
306+ data changes have been made.
307+
308+ XA Two-phase Commit Protocol
309+ ----------------------------
310
311 During statement execution, whenever any of data-modifying
312 PSEA API methods is used, e.g. Cursor::write_row() or
313@@ -305,47 +347,49 @@
314 Additional notes on DDL and the normal transaction.
315 ---------------------------------------------------
316
317- DDLs and operations with non-transactional engines
318- do not "register" in session->transaction lists, and thus do not
319- modify the transaction state. Besides, each DDL in
320- MySQL is prefixed with an implicit normal transaction commit
321- (a call to Session::endActiveTransaction()), and thus leaves nothing
322- to modify.
323- However, as it has been pointed out with CREATE TABLE .. SELECT,
324- some DDL statements can start a *new* transaction.
325+ CREATE TABLE .. SELECT can start a *new* normal transaction
326+ because of the fact that SELECTs on a transactional storage
327+ engine participate in the normal SQL transaction (due to
328+ isolation level issues and consistent read views).
329
330 Behaviour of the server in this case is currently badly
331 defined.
332+
333 DDL statements use a form of "semantic" logging
334 to maintain atomicity: if CREATE TABLE .. SELECT failed,
335 the newly created table is deleted.
336+
337 In addition, some DDL statements issue interim transaction
338- commits: e.g. ALTER Table issues a commit after data is copied
339+ commits: e.g. ALTER TABLE issues a COMMIT after data is copied
340 from the original table to the internal temporary table. Other
341 statements, e.g. CREATE TABLE ... SELECT do not always commit
342 after itself.
343+
344 And finally there is a group of DDL statements such as
345- RENAME/DROP Table that doesn't start a new transaction
346+ RENAME/DROP TABLE that doesn't start a new transaction
347 and doesn't commit.
348
349- This diversity makes it hard to say what will happen if
350- by chance a stored function is invoked during a DDL --
351- whether any modifications it makes will be committed or not
352- is not clear. Fortunately, SQL grammar of few DDLs allows
353- invocation of a stored function.
354-
355 A consistent behaviour is perhaps to always commit the normal
356 transaction after all DDLs, just like the statement transaction
357 is always committed at the end of all statements.
358 */
359-
360 void TransactionServices::registerResourceForStatement(Session *session,
361 plugin::TransactionalStorageEngine *engine)
362 {
363+ if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
364+ {
365+ /*
366+ * Now we automatically register this resource manager for the
367+ * normal transaction. This is fine because a statement
368+ * transaction registration should always enlist the resource
369+ * in the normal transaction which contains the statement
370+ * transaction.
371+ */
372+ registerResourceForTransaction(session, engine);
373+ }
374+
375 TransactionContext *trans= &session->transaction.stmt;
376- ResourceContext *resource_context;
377-
378- resource_context= session->getResourceContext(engine, 0);
379+ ResourceContext *resource_context= session->getResourceContext(engine, 0);
380
381 if (resource_context->isStarted())
382 return; /* already registered, return */
383@@ -356,38 +400,28 @@
384 trans->no_2pc|= not engine->hasTwoPhaseCommit();
385 }
386
387-/**
388- Register a storage engine for a transaction.
389-
390- Every storage engine MUST call this function when it starts
391- a transaction or a statement (that is it must be called both for the
392- "beginning of transaction" and "beginning of statement").
393- Only storage engines registered for the transaction/statement
394- will know when to commit/rollback it.
395-
396- @note
397- trans_register_ha is idempotent - storage engine may register many
398- times per transaction.
399-
400-*/
401-void TransactionServices::trans_register_ha(Session *session, plugin::TransactionalStorageEngine *engine)
402+void TransactionServices::registerResourceForTransaction(Session *session,
403+ plugin::TransactionalStorageEngine *engine)
404 {
405 TransactionContext *trans= &session->transaction.all;
406- ResourceContext *resource_context;
407+ ResourceContext *resource_context= session->getResourceContext(engine, 1);
408+
409+ if (resource_context->isStarted())
410+ return; /* already registered, return */
411
412 session->server_status|= SERVER_STATUS_IN_TRANS;
413
414- resource_context= session->getResourceContext(engine, 1);
415-
416- if (resource_context->isStarted())
417- return; /* already registered, return */
418-
419 resource_context->setResource(engine);
420 trans->registerResource(resource_context);
421
422 trans->no_2pc|= not engine->hasTwoPhaseCommit();
423+
424 if (session->transaction.xid_state.xid.is_null())
425 session->transaction.xid_state.xid.set(session->getQueryId());
426+
427+ /* Only true if user is executing a BEGIN WORK/START TRANSACTION */
428+ if (! session->getResourceContext(engine, 0)->isStarted())
429+ registerResourceForStatement(session, engine);
430 }
431
432 /**
433@@ -555,6 +589,7 @@
434 {
435 int err;
436 ResourceContext *resource_context= *it;
437+
438 plugin::TransactionalStorageEngine *engine= static_cast<plugin::TransactionalStorageEngine *>(resource_context->getResource());
439 if ((err= engine->commit(session, normal_transaction)))
440 {
441@@ -564,7 +599,6 @@
442 status_var_increment(session->status_var.ha_commit_count);
443 resource_context->reset(); /* keep it conveniently zero-filled */
444 }
445- trans->reset();
446
447 if (is_real_trans)
448 session->transaction.xid_state.xid.null();
449@@ -575,6 +609,7 @@
450 session->transaction.cleanup();
451 }
452 }
453+ trans->reset();
454 if (error == 0)
455 {
456 if (is_real_trans)
457@@ -613,16 +648,16 @@
458 {
459 int err;
460 ResourceContext *resource_context= *it;
461+
462 plugin::TransactionalStorageEngine *engine= static_cast<plugin::TransactionalStorageEngine *>(resource_context->getResource());
463 if ((err= engine->rollback(session, normal_transaction)))
464- { // cannot happen
465+ {
466 my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
467 error=1;
468 }
469 status_var_increment(session->status_var.ha_rollback_count);
470 resource_context->reset(); /* keep it conveniently zero-filled */
471 }
472- trans->reset();
473
474 /*
475 * We need to signal the ROLLBACK to ReplicationServices here
476@@ -646,14 +681,8 @@
477 session->transaction_rollback_request= false;
478
479 /*
480- If a non-transactional table was updated, warn; don't warn if this is a
481- slave thread (because when a slave thread executes a ROLLBACK, it has
482- been read from the binary log, so it's 100% sure and normal to produce
483- error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
484- slave SQL thread, it would not stop the thread but just be printed in
485- the error log; but we don't want users to wonder why they have this
486- message in the error log, so we don't send it.
487- */
488+ * If a non-transactional table was updated, warn the user
489+ */
490 if (is_real_trans &&
491 session->transaction.all.hasModifiedNonTransData() &&
492 session->killed != Session::KILL_CONNECTION)
493@@ -662,6 +691,7 @@
494 ER_WARNING_NOT_COMPLETE_ROLLBACK,
495 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
496 }
497+ trans->reset();
498 return error;
499 }
500
501
502=== modified file 'drizzled/transaction_services.h'
503--- drizzled/transaction_services.h 2010-02-25 04:43:14 +0000
504+++ drizzled/transaction_services.h 2010-02-25 04:43:15 +0000
505@@ -2,6 +2,7 @@
506 * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
507 *
508 * Copyright (C) 2008 Sun Microsystems
509+ * Copyright (c) Jay Pipes <jaypipes@gmail.com>
510 *
511 * This program is free software; you can redistribute it and/or modify
512 * it under the terms of the GNU General Public License as published by
513@@ -91,8 +92,32 @@
514 void registerResourceForStatement(Session *session,
515 plugin::TransactionalStorageEngine *engine);
516
517- /* these are called by storage engines */
518- void trans_register_ha(Session *session, plugin::TransactionalStorageEngine *engine);
519+ /**
520+ * Registers a resource manager in the "normal" transaction.
521+ *
522+ * @note
523+ *
524+ * This method is idempotent and must be idempotent
525+ * because it can be called both by the above
526+ * TransactionServices::registerResourceForStatement(),
527+ * which occurs at the beginning of each SQL statement,
528+ * and also manually when a BEGIN WORK/START TRANSACTION
529+ * statement is executed. If the latter case (BEGIN WORK)
530+ * is called, then subsequent contained statement transactions
531+ * will call this method as well.
532+ *
533+ * @note
534+ *
535+ * This method checks to see if the supplied resource
536+ * is also registered in the statement transaction, and
537+ * if not, registers the resource in the statement
538+ * transaction. This happens ONLY when the user has
539+ * called BEGIN WORK/START TRANSACTION, which is the only
540+ * time when this method is called except from the
541+ * TransactionServices::registerResourceForStatement method.
542+ */
543+ void registerResourceForTransaction(Session *session,
544+ plugin::TransactionalStorageEngine *engine);
545 };
546
547 } /* namespace drizzled */
548
549=== modified file 'plugin/innobase/handler/ha_innodb.cc'
550--- plugin/innobase/handler/ha_innodb.cc 2010-02-25 04:43:14 +0000
551+++ plugin/innobase/handler/ha_innodb.cc 2010-02-25 04:43:15 +0000
552@@ -270,6 +270,7 @@
553 addAlias("INNOBASE");
554 }
555 private:
556+ virtual int doStartTransaction(Session *session, start_transaction_option_t options);
557 virtual void doStartStatement(Session *session);
558 virtual void doEndStatement(Session *session);
559 public:
560@@ -348,18 +349,6 @@
561 the database name: for example, in 'mysql/data/test'
562 the database name is 'test' */
563
564- /*********************************************************************
565- Creates an InnoDB transaction struct for the session if it does not yet have one.
566- Starts a new InnoDB transaction if a transaction is not yet started. And
567- assigns a new snapshot for a consistent read if the transaction does not yet
568- have one. */
569- virtual
570- int
571- doStartConsistentSnapshot(
572- /*====================================*/
573- /* out: 0 */
574- Session* session); /* in: MySQL thread handle of the user for whom
575- the transaction should be committed */
576 /********************************************************************
577 Flushes InnoDB logs to disk and makes a checkpoint. Really, a commit flushes
578 the logs, and the name of this function should be innobase_checkpoint. */
579@@ -1506,27 +1495,6 @@
580 update_session(session);
581 }
582
583-/*********************************************************************//**
584-Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
585-to call the InnoDB prepare and commit, or rollback for the transaction. This
586-MUST be called for every transaction for which the user may call commit or
587-rollback. Calling this several times to register the same transaction is
588-allowed, too.
589-This function also registers the current SQL statement. */
590-static inline
591-void
592-innobase_register_trx_and_stmt(
593-/*===========================*/
594- plugin::TransactionalStorageEngine *engine, /*!< in: Innobase StorageEngine */
595- Session* session) /*!< in: MySQL thd (connection) object */
596-{
597- if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
598- /* No autocommit mode, register for a transaction */
599- TransactionServices &transaction_services= TransactionServices::singleton();
600- transaction_services.trans_register_ha(session, engine);
601- }
602-}
603-
604 /*****************************************************************//**
605 Convert an SQL identifier to the MySQL system_charset_info (UTF-8)
606 and quote it if needed.
607@@ -2089,41 +2057,35 @@
608 have one.
609 @return 0 */
610 int
611-InnobaseEngine::doStartConsistentSnapshot(
612+InnobaseEngine::doStartTransaction(
613 /*====================================*/
614- Session* session) /*!< in: MySQL thread handle of the user for whom
615- the transaction should be committed */
616+ Session* session, /*!< in: MySQL thread handle of the user for whom
617+ the transaction should be committed */
618+ start_transaction_option_t options)
619 {
620- trx_t* trx;
621-
622 assert(this == innodb_engine_ptr);
623
624 /* Create a new trx struct for session, if it does not yet have one */
625-
626- trx = check_trx_exists(session);
627+ trx_t *trx = check_trx_exists(session);
628
629 /* This is just to play safe: release a possible FIFO ticket and
630 search latch. Since we will reserve the kernel mutex, we have to
631 release the search system latch first to obey the latching order. */
632-
633 innobase_release_stat_resources(trx);
634
635 /* If the transaction is not started yet, start it */
636-
637 trx_start_if_not_started(trx);
638
639 /* Assign a read view if the transaction does not have it yet */
640-
641- trx_assign_read_view(trx);
642-
643- /* Set the MySQL flag to mark that there is an active transaction */
644-
645+ if (options == START_TRANS_OPT_WITH_CONS_SNAPSHOT)
646+ trx_assign_read_view(trx);
647+
648+ /* Set the Drizzle flag to mark that there is an active transaction */
649 if (trx->active_trans == 0) {
650- innobase_register_trx_and_stmt(this, current_session);
651- trx->active_trans = 1;
652+ trx->active_trans= 1;
653 }
654
655- return(0);
656+ return 0;
657 }
658
659 /*****************************************************************//**
660@@ -2153,9 +2115,10 @@
661
662 /* The flag trx->active_trans is set to 1 in
663
664- 1. ::external_lock(),
665- 4. InnobaseEngine::setSavepoint(),
666- 6. InnobaseEngine::doStartConsistentSnapshot(),
667+ 1. ::external_lock()
668+ 2 InnobaseEngine::doStartStatement()
669+ 3. InnobaseEngine::setSavepoint()
670+ 4. InnobaseEngine::doStartTransaction()
671
672 and it is only set to 0 in a commit or a rollback. If it is 0 we know
673 there cannot be resources to be freed and we could return immediately.
674@@ -8031,7 +7994,6 @@
675 * @todo this should go away
676 */
677 if (trx->active_trans == 0) {
678- innobase_register_trx_and_stmt(innodb_engine_ptr, session);
679 trx->active_trans= 1;
680 }
681 }
682@@ -8053,6 +8015,7 @@
683 if (trx->active_trans != 0)
684 {
685 commit(session, TRUE);
686+ trx->active_trans= 0;
687 }
688 }
689 else
690@@ -8065,8 +8028,6 @@
691 read_view_close_for_mysql(trx);
692 }
693 }
694-
695- trx->active_trans= 0;
696 }
697
698 /*******************************************************************//**
699
700=== added file 'tests/r/transaction.result'
701--- tests/r/transaction.result 1970-01-01 00:00:00 +0000
702+++ tests/r/transaction.result 2010-02-25 04:43:15 +0000
703@@ -0,0 +1,43 @@
704+DROP TABLE IF EXISTS t1_trx, t1_non_trx;
705+SET AUTOCOMMIT= 0;
706+CREATE TABLE t1_trx (
707+k VARCHAR(10) NOT NULL
708+, v VARCHAR(10) NOT NULL
709+, PRIMARY KEY (k)
710+) ENGINE=InnoDB;
711+CREATE TEMPORARY TABLE t1_non_trx (
712+k VARCHAR(10) NOT NULL
713+, v VARCHAR(10) NOT NULL
714+, PRIMARY KEY (k)
715+) ENGINE=MyISAM;
716+START TRANSACTION;
717+INSERT INTO t1_trx VALUES ('key1','value1');
718+INSERT INTO t1_trx VALUES ('key2','value2');
719+INSERT INTO t1_non_trx VALUES ('key1','value1');
720+INSERT INTO t1_non_trx VALUES ('key2','value2');
721+ROLLBACK;
722+Warnings:
723+Warning 1196 Some non-transactional changed tables couldn't be rolled back
724+Expected warning about non-trx data changes not being rolled back
725+SELECT * FROM t1_trx;
726+k v
727+SELECT * FROM t1_non_trx;
728+k v
729+key1 value1
730+key2 value2
731+START TRANSACTION;
732+INSERT INTO t1_trx VALUES ('key1','value1');
733+INSERT INTO t1_trx VALUES ('key2','value2');
734+SELECT t1_trx.k, t1_trx.v
735+FROM t1_trx
736+INNER JOIN t1_non_trx ON t1_trx.k = t1_non_trx.k;
737+k v
738+key1 value1
739+key2 value2
740+ROLLBACK;
741+SELECT t1_trx.k, t1_trx.v
742+FROM t1_trx
743+INNER JOIN t1_non_trx ON t1_trx.k = t1_non_trx.k;
744+k v
745+DROP TABLE t1_trx;
746+DROP TABLE t1_non_trx;
747
748=== added file 'tests/t/transaction.test'
749--- tests/t/transaction.test 1970-01-01 00:00:00 +0000
750+++ tests/t/transaction.test 2010-02-25 04:43:15 +0000
751@@ -0,0 +1,56 @@
752+# Tests a number of things related to transactions:
753+#
754+# 1. Interaction of more than one engine in a transaction
755+# 2. Correct commit and rollback behaviour
756+# 3. XA protocol communication and recovery
757+
758+--disable_warnings
759+DROP TABLE IF EXISTS t1_trx, t1_non_trx;
760+--enable_warnings
761+
762+SET AUTOCOMMIT= 0;
763+
764+CREATE TABLE t1_trx (
765+ k VARCHAR(10) NOT NULL
766+, v VARCHAR(10) NOT NULL
767+, PRIMARY KEY (k)
768+) ENGINE=InnoDB;
769+
770+CREATE TEMPORARY TABLE t1_non_trx (
771+ k VARCHAR(10) NOT NULL
772+, v VARCHAR(10) NOT NULL
773+, PRIMARY KEY (k)
774+) ENGINE=MyISAM;
775+
776+START TRANSACTION;
777+
778+INSERT INTO t1_trx VALUES ('key1','value1');
779+INSERT INTO t1_trx VALUES ('key2','value2');
780+
781+INSERT INTO t1_non_trx VALUES ('key1','value1');
782+INSERT INTO t1_non_trx VALUES ('key2','value2');
783+
784+ROLLBACK;
785+
786+--echo Expected warning about non-trx data changes not being rolled back
787+
788+SELECT * FROM t1_trx;
789+SELECT * FROM t1_non_trx;
790+
791+START TRANSACTION;
792+
793+INSERT INTO t1_trx VALUES ('key1','value1');
794+INSERT INTO t1_trx VALUES ('key2','value2');
795+
796+SELECT t1_trx.k, t1_trx.v
797+FROM t1_trx
798+INNER JOIN t1_non_trx ON t1_trx.k = t1_non_trx.k;
799+
800+ROLLBACK;
801+
802+SELECT t1_trx.k, t1_trx.v
803+FROM t1_trx
804+INNER JOIN t1_non_trx ON t1_trx.k = t1_non_trx.k;
805+
806+DROP TABLE t1_trx;
807+DROP TABLE t1_non_trx;