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