Merge lp:~dshrews/drizzle/bug660779 into lp:~drizzle-trunk/drizzle/development
- bug660779
- Merge into development
Proposed by
David Shrewsbury
Status: | Merged |
---|---|
Approved by: | Brian Aker |
Approved revision: | 1869 |
Merge reported by: | Lee Bieber |
Merged at revision: | not available |
Proposed branch: | lp:~dshrews/drizzle/bug660779 |
Merge into: | lp:~drizzle-trunk/drizzle/development |
Diff against target: |
716 lines (+512/-48) 9 files modified
drizzled/session.cc (+14/-14) drizzled/session.h (+45/-24) drizzled/sql_insert.cc (+9/-0) drizzled/sql_update.cc (+22/-8) drizzled/transaction_services.cc (+121/-2) drizzled/transaction_services.h (+22/-0) plugin/transaction_log/tests/r/bug660779.result (+220/-0) plugin/transaction_log/tests/t/bug660779-master.opt (+1/-0) plugin/transaction_log/tests/t/bug660779.test (+58/-0) |
To merge this branch: | bzr merge lp:~dshrews/drizzle/bug660779 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Drizzle Merge Team | Pending | ||
Review via email: mp+39375@code.launchpad.net |
This proposal supersedes a proposal from 2010-10-25.
Commit message
Description of the change
Provides methods in TransactionServices to undo adding records to the current Statement message when a multi-row affecting statement fails mid-execution. Call this method for UPDATEs and INSERT..SELECTs that fail.
Also cleans up some #includes in session.* and adds some comments for the st_transactions struct in session.h.
To post a comment you must log in.
Revision history for this message
Lee Bieber (kalebral-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Revision history for this message
David Shrewsbury (dshrews) wrote : Posted in a previous version of this proposal | # |
Grr... working on it...
Revision history for this message
David Shrewsbury (dshrews) wrote : | # |
Solaris succeeded in param-build.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'drizzled/session.cc' | |||
2 | --- drizzled/session.cc 2010-10-22 22:08:54 +0000 | |||
3 | +++ drizzled/session.cc 2010-10-26 14:47:53 +0000 | |||
4 | @@ -22,21 +22,21 @@ | |||
5 | 22 | */ | 22 | */ |
6 | 23 | 23 | ||
7 | 24 | #include "config.h" | 24 | #include "config.h" |
9 | 25 | #include <drizzled/session.h> | 25 | #include "drizzled/session.h" |
10 | 26 | #include "drizzled/session_list.h" | 26 | #include "drizzled/session_list.h" |
11 | 27 | #include <sys/stat.h> | 27 | #include <sys/stat.h> |
24 | 28 | #include <drizzled/error.h> | 28 | #include "drizzled/error.h" |
25 | 29 | #include <drizzled/gettext.h> | 29 | #include "drizzled/gettext.h" |
26 | 30 | #include <drizzled/query_id.h> | 30 | #include "drizzled/query_id.h" |
27 | 31 | #include <drizzled/data_home.h> | 31 | #include "drizzled/data_home.h" |
28 | 32 | #include <drizzled/sql_base.h> | 32 | #include "drizzled/sql_base.h" |
29 | 33 | #include <drizzled/lock.h> | 33 | #include "drizzled/lock.h" |
30 | 34 | #include <drizzled/item/cache.h> | 34 | #include "drizzled/item/cache.h" |
31 | 35 | #include <drizzled/item/float.h> | 35 | #include "drizzled/item/float.h" |
32 | 36 | #include <drizzled/item/return_int.h> | 36 | #include "drizzled/item/return_int.h" |
33 | 37 | #include <drizzled/item/empty_string.h> | 37 | #include "drizzled/item/empty_string.h" |
34 | 38 | #include <drizzled/show.h> | 38 | #include "drizzled/show.h" |
35 | 39 | #include <drizzled/plugin/client.h> | 39 | #include "drizzled/plugin/client.h" |
36 | 40 | #include "drizzled/plugin/scheduler.h" | 40 | #include "drizzled/plugin/scheduler.h" |
37 | 41 | #include "drizzled/plugin/authentication.h" | 41 | #include "drizzled/plugin/authentication.h" |
38 | 42 | #include "drizzled/plugin/logging.h" | 42 | #include "drizzled/plugin/logging.h" |
39 | @@ -60,7 +60,7 @@ | |||
40 | 60 | #include <fcntl.h> | 60 | #include <fcntl.h> |
41 | 61 | #include <algorithm> | 61 | #include <algorithm> |
42 | 62 | #include <climits> | 62 | #include <climits> |
44 | 63 | #include "boost/filesystem.hpp" | 63 | #include <boost/filesystem.hpp> |
45 | 64 | 64 | ||
46 | 65 | using namespace std; | 65 | using namespace std; |
47 | 66 | 66 | ||
48 | 67 | 67 | ||
49 | === modified file 'drizzled/session.h' | |||
50 | --- drizzled/session.h 2010-10-23 22:18:01 +0000 | |||
51 | +++ drizzled/session.h 2010-10-26 14:47:53 +0000 | |||
52 | @@ -21,23 +21,20 @@ | |||
53 | 21 | #ifndef DRIZZLED_SESSION_H | 21 | #ifndef DRIZZLED_SESSION_H |
54 | 22 | #define DRIZZLED_SESSION_H | 22 | #define DRIZZLED_SESSION_H |
55 | 23 | 23 | ||
56 | 24 | /* Classes in mysql */ | ||
57 | 25 | |||
58 | 26 | #include "drizzled/plugin.h" | 24 | #include "drizzled/plugin.h" |
60 | 27 | #include <drizzled/sql_locale.h> | 25 | #include "drizzled/sql_locale.h" |
61 | 28 | #include "drizzled/resource_context.h" | 26 | #include "drizzled/resource_context.h" |
69 | 29 | #include <drizzled/cursor.h> | 27 | #include "drizzled/cursor.h" |
70 | 30 | #include <drizzled/current_session.h> | 28 | #include "drizzled/current_session.h" |
71 | 31 | #include <drizzled/sql_error.h> | 29 | #include "drizzled/sql_error.h" |
72 | 32 | #include <drizzled/file_exchange.h> | 30 | #include "drizzled/file_exchange.h" |
73 | 33 | #include <drizzled/select_result_interceptor.h> | 31 | #include "drizzled/select_result_interceptor.h" |
74 | 34 | #include <drizzled/statistics_variables.h> | 32 | #include "drizzled/statistics_variables.h" |
75 | 35 | #include <drizzled/xid.h> | 33 | #include "drizzled/xid.h" |
76 | 36 | #include "drizzled/query_id.h" | 34 | #include "drizzled/query_id.h" |
77 | 37 | #include "drizzled/named_savepoint.h" | 35 | #include "drizzled/named_savepoint.h" |
78 | 38 | #include "drizzled/transaction_context.h" | 36 | #include "drizzled/transaction_context.h" |
79 | 39 | #include "drizzled/util/storable.h" | 37 | #include "drizzled/util/storable.h" |
80 | 40 | |||
81 | 41 | #include "drizzled/my_hash.h" | 38 | #include "drizzled/my_hash.h" |
82 | 42 | 39 | ||
83 | 43 | #include <netdb.h> | 40 | #include <netdb.h> |
84 | @@ -47,14 +44,11 @@ | |||
85 | 47 | #include <deque> | 44 | #include <deque> |
86 | 48 | 45 | ||
87 | 49 | #include "drizzled/internal/getrusage.h" | 46 | #include "drizzled/internal/getrusage.h" |
96 | 50 | 47 | #include "drizzled/security_context.h" | |
97 | 51 | #include <drizzled/security_context.h> | 48 | #include "drizzled/open_tables_state.h" |
98 | 52 | #include <drizzled/open_tables_state.h> | 49 | #include "drizzled/internal_error_handler.h" |
99 | 53 | 50 | #include "drizzled/diagnostics_area.h" | |
100 | 54 | #include <drizzled/internal_error_handler.h> | 51 | #include "drizzled/plugin/authorization.h" |
93 | 55 | #include <drizzled/diagnostics_area.h> | ||
94 | 56 | |||
95 | 57 | #include <drizzled/plugin/authorization.h> | ||
101 | 58 | 52 | ||
102 | 59 | #include <boost/unordered_map.hpp> | 53 | #include <boost/unordered_map.hpp> |
103 | 60 | #include <boost/thread/mutex.hpp> | 54 | #include <boost/thread/mutex.hpp> |
104 | @@ -550,10 +544,38 @@ | |||
105 | 550 | ResourceContext *getResourceContext(const plugin::MonitoredInTransaction *monitored, | 544 | ResourceContext *getResourceContext(const plugin::MonitoredInTransaction *monitored, |
106 | 551 | size_t index= 0); | 545 | size_t index= 0); |
107 | 552 | 546 | ||
108 | 547 | /** | ||
109 | 548 | * Structure used to manage "statement transactions" and | ||
110 | 549 | * "normal transactions". In autocommit mode, the normal transaction is | ||
111 | 550 | * equivalent to the statement transaction. | ||
112 | 551 | * | ||
113 | 552 | * Storage engines will be registered here when they participate in | ||
114 | 553 | * a transaction. No engine is registered more than once. | ||
115 | 554 | */ | ||
116 | 553 | struct st_transactions { | 555 | struct st_transactions { |
117 | 554 | std::deque<NamedSavepoint> savepoints; | 556 | std::deque<NamedSavepoint> savepoints; |
120 | 555 | TransactionContext all; ///< Trans since BEGIN WORK | 557 | |
121 | 556 | TransactionContext stmt; ///< Trans for current statement | 558 | /** |
122 | 559 | * The normal transaction (since BEGIN WORK). | ||
123 | 560 | * | ||
124 | 561 | * Contains a list of all engines that have participated in any of the | ||
125 | 562 | * statement transactions started within the context of the normal | ||
126 | 563 | * transaction. | ||
127 | 564 | * | ||
128 | 565 | * @note In autocommit mode, this is empty. | ||
129 | 566 | */ | ||
130 | 567 | TransactionContext all; | ||
131 | 568 | |||
132 | 569 | /** | ||
133 | 570 | * The statment transaction. | ||
134 | 571 | * | ||
135 | 572 | * Contains a list of all engines participating in the given statement. | ||
136 | 573 | * | ||
137 | 574 | * @note In autocommit mode, this will be used to commit/rollback the | ||
138 | 575 | * normal transaction. | ||
139 | 576 | */ | ||
140 | 577 | TransactionContext stmt; | ||
141 | 578 | |||
142 | 557 | XID_STATE xid_state; | 579 | XID_STATE xid_state; |
143 | 558 | 580 | ||
144 | 559 | void cleanup() | 581 | void cleanup() |
145 | @@ -1197,9 +1219,8 @@ | |||
146 | 1197 | * Current implementation does not depend on that, but future changes | 1219 | * Current implementation does not depend on that, but future changes |
147 | 1198 | * should be done with this in mind; | 1220 | * should be done with this in mind; |
148 | 1199 | * | 1221 | * |
152 | 1200 | * @param Scrambled password received from client | 1222 | * @param passwd Scrambled password received from client |
153 | 1201 | * @param Length of scrambled password | 1223 | * @param db Database name to connect to, may be NULL |
151 | 1202 | * @param Database name to connect to, may be NULL | ||
154 | 1203 | */ | 1224 | */ |
155 | 1204 | bool checkUser(const std::string &passwd, const std::string &db); | 1225 | bool checkUser(const std::string &passwd, const std::string &db); |
156 | 1205 | 1226 | ||
157 | 1206 | 1227 | ||
158 | === modified file 'drizzled/sql_insert.cc' | |||
159 | --- drizzled/sql_insert.cc 2010-10-24 19:36:55 +0000 | |||
160 | +++ drizzled/sql_insert.cc 2010-10-26 14:47:53 +0000 | |||
161 | @@ -1276,7 +1276,16 @@ | |||
162 | 1276 | store_values(values); | 1276 | store_values(values); |
163 | 1277 | session->count_cuted_fields= CHECK_FIELD_IGNORE; | 1277 | session->count_cuted_fields= CHECK_FIELD_IGNORE; |
164 | 1278 | if (session->is_error()) | 1278 | if (session->is_error()) |
165 | 1279 | { | ||
166 | 1280 | /* | ||
167 | 1281 | * If we fail mid-way through INSERT..SELECT, we need to remove any | ||
168 | 1282 | * records that we added to the current Statement message. We can | ||
169 | 1283 | * use session->row_count to know how many records we have already added. | ||
170 | 1284 | */ | ||
171 | 1285 | TransactionServices &ts= TransactionServices::singleton(); | ||
172 | 1286 | ts.removeStatementRecords(session, (session->row_count - 1)); | ||
173 | 1279 | return(1); | 1287 | return(1); |
174 | 1288 | } | ||
175 | 1280 | 1289 | ||
176 | 1281 | // Release latches in case bulk insert takes a long time | 1290 | // Release latches in case bulk insert takes a long time |
177 | 1282 | plugin::TransactionalStorageEngine::releaseTemporaryLatches(session); | 1291 | plugin::TransactionalStorageEngine::releaseTemporaryLatches(session); |
178 | 1283 | 1292 | ||
179 | === modified file 'drizzled/sql_update.cc' | |||
180 | --- drizzled/sql_update.cc 2010-10-24 01:34:39 +0000 | |||
181 | +++ drizzled/sql_update.cc 2010-10-26 14:47:53 +0000 | |||
182 | @@ -29,6 +29,7 @@ | |||
183 | 29 | #include "drizzled/records.h" | 29 | #include "drizzled/records.h" |
184 | 30 | #include "drizzled/internal/my_sys.h" | 30 | #include "drizzled/internal/my_sys.h" |
185 | 31 | #include "drizzled/internal/iocache.h" | 31 | #include "drizzled/internal/iocache.h" |
186 | 32 | #include "drizzled/transaction_services.h" | ||
187 | 32 | 33 | ||
188 | 33 | #include <boost/dynamic_bitset.hpp> | 34 | #include <boost/dynamic_bitset.hpp> |
189 | 34 | #include <list> | 35 | #include <list> |
190 | @@ -473,7 +474,20 @@ | |||
191 | 473 | 474 | ||
192 | 474 | table->storeRecord(); | 475 | table->storeRecord(); |
193 | 475 | if (fill_record(session, fields, values)) | 476 | if (fill_record(session, fields, values)) |
194 | 477 | { | ||
195 | 478 | /* | ||
196 | 479 | * If we updated some rows before this one failed (updated > 0), | ||
197 | 480 | * then we will need to undo adding those records to the | ||
198 | 481 | * replication Statement message. | ||
199 | 482 | */ | ||
200 | 483 | if (updated > 0) | ||
201 | 484 | { | ||
202 | 485 | TransactionServices &ts= TransactionServices::singleton(); | ||
203 | 486 | ts.removeStatementRecords(session, updated); | ||
204 | 487 | } | ||
205 | 488 | |||
206 | 476 | break; | 489 | break; |
207 | 490 | } | ||
208 | 477 | 491 | ||
209 | 478 | found++; | 492 | found++; |
210 | 479 | 493 | ||
211 | @@ -486,15 +500,15 @@ | |||
212 | 486 | table->auto_increment_field_not_null= false; | 500 | table->auto_increment_field_not_null= false; |
213 | 487 | 501 | ||
214 | 488 | if (!error || error == HA_ERR_RECORD_IS_THE_SAME) | 502 | if (!error || error == HA_ERR_RECORD_IS_THE_SAME) |
216 | 489 | { | 503 | { |
217 | 490 | if (error != HA_ERR_RECORD_IS_THE_SAME) | 504 | if (error != HA_ERR_RECORD_IS_THE_SAME) |
218 | 491 | updated++; | 505 | updated++; |
219 | 492 | else | 506 | else |
220 | 493 | error= 0; | 507 | error= 0; |
223 | 494 | } | 508 | } |
224 | 495 | else if (! ignore || | 509 | else if (! ignore || |
225 | 496 | table->cursor->is_fatal_error(error, HA_CHECK_DUP_KEY)) | 510 | table->cursor->is_fatal_error(error, HA_CHECK_DUP_KEY)) |
227 | 497 | { | 511 | { |
228 | 498 | /* | 512 | /* |
229 | 499 | If (ignore && error is ignorable) we don't have to | 513 | If (ignore && error is ignorable) we don't have to |
230 | 500 | do anything; otherwise... | 514 | do anything; otherwise... |
231 | @@ -505,10 +519,10 @@ | |||
232 | 505 | flags|= ME_FATALERROR; /* Other handler errors are fatal */ | 519 | flags|= ME_FATALERROR; /* Other handler errors are fatal */ |
233 | 506 | 520 | ||
234 | 507 | prepare_record_for_error_message(error, table); | 521 | prepare_record_for_error_message(error, table); |
239 | 508 | table->print_error(error,MYF(flags)); | 522 | table->print_error(error,MYF(flags)); |
240 | 509 | error= 1; | 523 | error= 1; |
241 | 510 | break; | 524 | break; |
242 | 511 | } | 525 | } |
243 | 512 | } | 526 | } |
244 | 513 | 527 | ||
245 | 514 | if (!--limit && using_limit) | 528 | if (!--limit && using_limit) |
246 | 515 | 529 | ||
247 | === modified file 'drizzled/transaction_services.cc' | |||
248 | --- drizzled/transaction_services.cc 2010-10-11 17:20:14 +0000 | |||
249 | +++ drizzled/transaction_services.cc 2010-10-26 14:47:53 +0000 | |||
250 | @@ -71,11 +71,13 @@ | |||
251 | 71 | #include "drizzled/plugin/xa_resource_manager.h" | 71 | #include "drizzled/plugin/xa_resource_manager.h" |
252 | 72 | #include "drizzled/internal/my_sys.h" | 72 | #include "drizzled/internal/my_sys.h" |
253 | 73 | 73 | ||
254 | 74 | using namespace std; | ||
255 | 75 | |||
256 | 76 | #include <vector> | 74 | #include <vector> |
257 | 77 | #include <algorithm> | 75 | #include <algorithm> |
258 | 78 | #include <functional> | 76 | #include <functional> |
259 | 77 | #include <google/protobuf/repeated_field.h> | ||
260 | 78 | |||
261 | 79 | using namespace std; | ||
262 | 80 | using namespace google; | ||
263 | 79 | 81 | ||
264 | 80 | namespace drizzled | 82 | namespace drizzled |
265 | 81 | { | 83 | { |
266 | @@ -1826,6 +1828,123 @@ | |||
267 | 1826 | } | 1828 | } |
268 | 1827 | } | 1829 | } |
269 | 1828 | 1830 | ||
270 | 1831 | |||
271 | 1832 | /** | ||
272 | 1833 | * Template for removing Statement records of different types. | ||
273 | 1834 | * | ||
274 | 1835 | * The code for removing records from different Statement message types | ||
275 | 1836 | * is identical except for the class types that are embedded within the | ||
276 | 1837 | * Statement. | ||
277 | 1838 | * | ||
278 | 1839 | * There are 3 scenarios we need to look for: | ||
279 | 1840 | * - We've been asked to remove more records than exist in the Statement | ||
280 | 1841 | * - We've been asked to remove less records than exist in the Statement | ||
281 | 1842 | * - We've been asked to remove ALL records that exist in the Statement | ||
282 | 1843 | * | ||
283 | 1844 | * If we are removing ALL records, then effectively we would be left with | ||
284 | 1845 | * an empty Statement message, so we should just remove it and clean up | ||
285 | 1846 | * message pointers in the Session object. | ||
286 | 1847 | */ | ||
287 | 1848 | template <class DataType, class RecordType> | ||
288 | 1849 | static bool removeStatementRecordsWithType(Session *session, | ||
289 | 1850 | DataType *data, | ||
290 | 1851 | uint32_t count) | ||
291 | 1852 | { | ||
292 | 1853 | uint32_t num_avail_recs= static_cast<uint32_t>(data->record_size()); | ||
293 | 1854 | |||
294 | 1855 | /* If there aren't enough records to remove 'count' of them, error. */ | ||
295 | 1856 | if (num_avail_recs < count) | ||
296 | 1857 | return false; | ||
297 | 1858 | |||
298 | 1859 | /* | ||
299 | 1860 | * If we are removing all of the data records, we'll just remove this | ||
300 | 1861 | * entire Statement message. | ||
301 | 1862 | */ | ||
302 | 1863 | if (num_avail_recs == count) | ||
303 | 1864 | { | ||
304 | 1865 | message::Transaction *transaction= session->getTransactionMessage(); | ||
305 | 1866 | protobuf::RepeatedPtrField<message::Statement> *statements= transaction->mutable_statement(); | ||
306 | 1867 | statements->RemoveLast(); | ||
307 | 1868 | |||
308 | 1869 | /* | ||
309 | 1870 | * Now need to set the Session Statement pointer to either the previous | ||
310 | 1871 | * Statement, or NULL if there isn't one. | ||
311 | 1872 | */ | ||
312 | 1873 | if (statements->size() == 0) | ||
313 | 1874 | { | ||
314 | 1875 | session->setStatementMessage(NULL); | ||
315 | 1876 | } | ||
316 | 1877 | else | ||
317 | 1878 | { | ||
318 | 1879 | /* | ||
319 | 1880 | * There isn't a great way to get a pointer to the previous Statement | ||
320 | 1881 | * message using the RepeatedPtrField object, so we'll just get to it | ||
321 | 1882 | * using the Transaction message. | ||
322 | 1883 | */ | ||
323 | 1884 | int last_stmt_idx= transaction->statement_size() - 1; | ||
324 | 1885 | session->setStatementMessage(transaction->mutable_statement(last_stmt_idx)); | ||
325 | 1886 | } | ||
326 | 1887 | } | ||
327 | 1888 | /* We only need to remove 'count' records */ | ||
328 | 1889 | else if (num_avail_recs > count) | ||
329 | 1890 | { | ||
330 | 1891 | protobuf::RepeatedPtrField<RecordType> *records= data->mutable_record(); | ||
331 | 1892 | while (count--) | ||
332 | 1893 | records->RemoveLast(); | ||
333 | 1894 | } | ||
334 | 1895 | |||
335 | 1896 | return true; | ||
336 | 1897 | } | ||
337 | 1898 | |||
338 | 1899 | |||
339 | 1900 | bool TransactionServices::removeStatementRecords(Session *session, | ||
340 | 1901 | uint32_t count) | ||
341 | 1902 | { | ||
342 | 1903 | ReplicationServices &replication_services= ReplicationServices::singleton(); | ||
343 | 1904 | if (! replication_services.isActive()) | ||
344 | 1905 | return false; | ||
345 | 1906 | |||
346 | 1907 | /* Get the most current Statement */ | ||
347 | 1908 | message::Statement *statement= session->getStatementMessage(); | ||
348 | 1909 | |||
349 | 1910 | /* Make sure we have work to do */ | ||
350 | 1911 | if (statement == NULL) | ||
351 | 1912 | return false; | ||
352 | 1913 | |||
353 | 1914 | bool retval= false; | ||
354 | 1915 | |||
355 | 1916 | switch (statement->type()) | ||
356 | 1917 | { | ||
357 | 1918 | case message::Statement::INSERT: | ||
358 | 1919 | { | ||
359 | 1920 | message::InsertData *data= statement->mutable_insert_data(); | ||
360 | 1921 | retval= removeStatementRecordsWithType<message::InsertData, message::InsertRecord>(session, data, count); | ||
361 | 1922 | break; | ||
362 | 1923 | } | ||
363 | 1924 | |||
364 | 1925 | case message::Statement::UPDATE: | ||
365 | 1926 | { | ||
366 | 1927 | message::UpdateData *data= statement->mutable_update_data(); | ||
367 | 1928 | retval= removeStatementRecordsWithType<message::UpdateData, message::UpdateRecord>(session, data, count); | ||
368 | 1929 | break; | ||
369 | 1930 | } | ||
370 | 1931 | |||
371 | 1932 | case message::Statement::DELETE: /* not sure if this one is possible... */ | ||
372 | 1933 | { | ||
373 | 1934 | message::DeleteData *data= statement->mutable_delete_data(); | ||
374 | 1935 | retval= removeStatementRecordsWithType<message::DeleteData, message::DeleteRecord>(session, data, count); | ||
375 | 1936 | break; | ||
376 | 1937 | } | ||
377 | 1938 | |||
378 | 1939 | default: | ||
379 | 1940 | retval= false; | ||
380 | 1941 | break; | ||
381 | 1942 | } | ||
382 | 1943 | |||
383 | 1944 | return retval; | ||
384 | 1945 | } | ||
385 | 1946 | |||
386 | 1947 | |||
387 | 1829 | void TransactionServices::createTable(Session *in_session, | 1948 | void TransactionServices::createTable(Session *in_session, |
388 | 1830 | const message::Table &table) | 1949 | const message::Table &table) |
389 | 1831 | { | 1950 | { |
390 | 1832 | 1951 | ||
391 | === modified file 'drizzled/transaction_services.h' | |||
392 | --- drizzled/transaction_services.h 2010-10-11 17:20:14 +0000 | |||
393 | +++ drizzled/transaction_services.h 2010-10-26 14:47:53 +0000 | |||
394 | @@ -260,6 +260,28 @@ | |||
395 | 260 | * @param use_update_record If true, uses the values from the update row instead | 260 | * @param use_update_record If true, uses the values from the update row instead |
396 | 261 | */ | 261 | */ |
397 | 262 | void deleteRecord(Session *in_session, Table *in_table, bool use_update_record= false); | 262 | void deleteRecord(Session *in_session, Table *in_table, bool use_update_record= false); |
398 | 263 | |||
399 | 264 | /** | ||
400 | 265 | * Used to undo effects of a failed statement. | ||
401 | 266 | * | ||
402 | 267 | * An SQL statement, like an UPDATE, that affects multiple rows could | ||
403 | 268 | * potentially fail mid-way through processing the rows. In such a case, | ||
404 | 269 | * the successfully modified rows that preceeded the failing row would | ||
405 | 270 | * have been added to the Statement message. This method is used for | ||
406 | 271 | * rolling back that change. | ||
407 | 272 | * | ||
408 | 273 | * @note | ||
409 | 274 | * This particular failure is seen on column constraint violations | ||
410 | 275 | * during a multi-row UPDATE and a multi-row INSERT..SELECT. | ||
411 | 276 | * | ||
412 | 277 | * @param in_session Pointer to the Session containing the Statement | ||
413 | 278 | * @param count The number of records to remove from Statement. | ||
414 | 279 | * | ||
415 | 280 | * @retval true Successfully removed 'count' records | ||
416 | 281 | * @retval false Failure | ||
417 | 282 | */ | ||
418 | 283 | bool removeStatementRecords(Session *in_session, uint32_t count); | ||
419 | 284 | |||
420 | 263 | /** | 285 | /** |
421 | 264 | * Creates a CreateSchema Statement GPB message and adds it | 286 | * Creates a CreateSchema Statement GPB message and adds it |
422 | 265 | * to the Session's active Transaction GPB message for pushing | 287 | * to the Session's active Transaction GPB message for pushing |
423 | 266 | 288 | ||
424 | === added file 'plugin/transaction_log/tests/r/bug660779.result' | |||
425 | --- plugin/transaction_log/tests/r/bug660779.result 1970-01-01 00:00:00 +0000 | |||
426 | +++ plugin/transaction_log/tests/r/bug660779.result 2010-10-26 14:47:53 +0000 | |||
427 | @@ -0,0 +1,220 @@ | |||
428 | 1 | CREATE TABLE t1 ( | ||
429 | 2 | pk INT NOT NULL AUTO_INCREMENT, | ||
430 | 3 | col_int1 INT, | ||
431 | 4 | col_int2 INT, | ||
432 | 5 | col_int_not_null INT NOT NULL, | ||
433 | 6 | PRIMARY KEY (pk)); | ||
434 | 7 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (1,1,1); | ||
435 | 8 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (NULL,1,1); | ||
436 | 9 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (2,1,3); | ||
437 | 10 | SET GLOBAL transaction_log_truncate_debug= true; | ||
438 | 11 | BEGIN; | ||
439 | 12 | UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; | ||
440 | 13 | ERROR 23000: Column 'col_int_not_null' cannot be null | ||
441 | 14 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (5,5,5); | ||
442 | 15 | COMMIT; | ||
443 | 16 | |||
444 | 17 | We should have a Transaction with a single insert Statement | ||
445 | 18 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
446 | 19 | PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) | ||
447 | 20 | transaction_context { | ||
448 | 21 | server_id: 1 | ||
449 | 22 | transaction_id: 1 | ||
450 | 23 | START_TIMESTAMP | ||
451 | 24 | END_TIMESTAMP | ||
452 | 25 | } | ||
453 | 26 | statement { | ||
454 | 27 | type: INSERT | ||
455 | 28 | START_TIMESTAMP | ||
456 | 29 | END_TIMESTAMP | ||
457 | 30 | insert_header { | ||
458 | 31 | table_metadata { | ||
459 | 32 | schema_name: "test" | ||
460 | 33 | table_name: "t1" | ||
461 | 34 | } | ||
462 | 35 | field_metadata { | ||
463 | 36 | type: INTEGER | ||
464 | 37 | name: "pk" | ||
465 | 38 | } | ||
466 | 39 | field_metadata { | ||
467 | 40 | type: INTEGER | ||
468 | 41 | name: "col_int1" | ||
469 | 42 | } | ||
470 | 43 | field_metadata { | ||
471 | 44 | type: INTEGER | ||
472 | 45 | name: "col_int2" | ||
473 | 46 | } | ||
474 | 47 | field_metadata { | ||
475 | 48 | type: INTEGER | ||
476 | 49 | name: "col_int_not_null" | ||
477 | 50 | } | ||
478 | 51 | } | ||
479 | 52 | insert_data { | ||
480 | 53 | segment_id: 1 | ||
481 | 54 | end_segment: true | ||
482 | 55 | record { | ||
483 | 56 | insert_value: "4" | ||
484 | 57 | insert_value: "5" | ||
485 | 58 | insert_value: "5" | ||
486 | 59 | insert_value: "5" | ||
487 | 60 | is_null: false | ||
488 | 61 | is_null: false | ||
489 | 62 | is_null: false | ||
490 | 63 | is_null: false | ||
491 | 64 | } | ||
492 | 65 | } | ||
493 | 66 | } | ||
494 | 67 | |||
495 | 68 | BEGIN; | ||
496 | 69 | UPDATE t1 SET col_int1 = (col_int1 + 1) WHERE col_int2 = 1; | ||
497 | 70 | UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; | ||
498 | 71 | ERROR 23000: Column 'col_int_not_null' cannot be null | ||
499 | 72 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (6,6,6); | ||
500 | 73 | COMMIT; | ||
501 | 74 | |||
502 | 75 | We should have a Transaction with 1 update and 1 insert Statement | ||
503 | 76 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
504 | 77 | PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) | ||
505 | 78 | transaction_context { | ||
506 | 79 | server_id: 1 | ||
507 | 80 | transaction_id: 2 | ||
508 | 81 | START_TIMESTAMP | ||
509 | 82 | END_TIMESTAMP | ||
510 | 83 | } | ||
511 | 84 | statement { | ||
512 | 85 | type: UPDATE | ||
513 | 86 | START_TIMESTAMP | ||
514 | 87 | END_TIMESTAMP | ||
515 | 88 | update_header { | ||
516 | 89 | table_metadata { | ||
517 | 90 | schema_name: "test" | ||
518 | 91 | table_name: "t1" | ||
519 | 92 | } | ||
520 | 93 | key_field_metadata { | ||
521 | 94 | type: INTEGER | ||
522 | 95 | name: "pk" | ||
523 | 96 | } | ||
524 | 97 | set_field_metadata { | ||
525 | 98 | type: INTEGER | ||
526 | 99 | name: "col_int1" | ||
527 | 100 | } | ||
528 | 101 | } | ||
529 | 102 | update_data { | ||
530 | 103 | segment_id: 1 | ||
531 | 104 | end_segment: true | ||
532 | 105 | record { | ||
533 | 106 | key_value: "1" | ||
534 | 107 | after_value: "2" | ||
535 | 108 | is_null: false | ||
536 | 109 | } | ||
537 | 110 | record { | ||
538 | 111 | key_value: "3" | ||
539 | 112 | after_value: "3" | ||
540 | 113 | is_null: false | ||
541 | 114 | } | ||
542 | 115 | } | ||
543 | 116 | } | ||
544 | 117 | statement { | ||
545 | 118 | type: INSERT | ||
546 | 119 | START_TIMESTAMP | ||
547 | 120 | END_TIMESTAMP | ||
548 | 121 | insert_header { | ||
549 | 122 | table_metadata { | ||
550 | 123 | schema_name: "test" | ||
551 | 124 | table_name: "t1" | ||
552 | 125 | } | ||
553 | 126 | field_metadata { | ||
554 | 127 | type: INTEGER | ||
555 | 128 | name: "pk" | ||
556 | 129 | } | ||
557 | 130 | field_metadata { | ||
558 | 131 | type: INTEGER | ||
559 | 132 | name: "col_int1" | ||
560 | 133 | } | ||
561 | 134 | field_metadata { | ||
562 | 135 | type: INTEGER | ||
563 | 136 | name: "col_int2" | ||
564 | 137 | } | ||
565 | 138 | field_metadata { | ||
566 | 139 | type: INTEGER | ||
567 | 140 | name: "col_int_not_null" | ||
568 | 141 | } | ||
569 | 142 | } | ||
570 | 143 | insert_data { | ||
571 | 144 | segment_id: 1 | ||
572 | 145 | end_segment: true | ||
573 | 146 | record { | ||
574 | 147 | insert_value: "5" | ||
575 | 148 | insert_value: "6" | ||
576 | 149 | insert_value: "6" | ||
577 | 150 | insert_value: "6" | ||
578 | 151 | is_null: false | ||
579 | 152 | is_null: false | ||
580 | 153 | is_null: false | ||
581 | 154 | is_null: false | ||
582 | 155 | } | ||
583 | 156 | } | ||
584 | 157 | } | ||
585 | 158 | |||
586 | 159 | CREATE TABLE t2 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, a INT); | ||
587 | 160 | INSERT INTO t2 (a) VALUES (1),(2), (NULL); | ||
588 | 161 | BEGIN; | ||
589 | 162 | INSERT INTO t1 (col_int_not_null) SELECT a FROM t2; | ||
590 | 163 | ERROR 23000: Column 'col_int_not_null' cannot be null | ||
591 | 164 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (7,7,7); | ||
592 | 165 | COMMIT; | ||
593 | 166 | |||
594 | 167 | We should have a Transaction with 1 insert Statement | ||
595 | 168 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
596 | 169 | PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) | ||
597 | 170 | transaction_context { | ||
598 | 171 | server_id: 1 | ||
599 | 172 | transaction_id: 5 | ||
600 | 173 | START_TIMESTAMP | ||
601 | 174 | END_TIMESTAMP | ||
602 | 175 | } | ||
603 | 176 | statement { | ||
604 | 177 | type: INSERT | ||
605 | 178 | START_TIMESTAMP | ||
606 | 179 | END_TIMESTAMP | ||
607 | 180 | insert_header { | ||
608 | 181 | table_metadata { | ||
609 | 182 | schema_name: "test" | ||
610 | 183 | table_name: "t1" | ||
611 | 184 | } | ||
612 | 185 | field_metadata { | ||
613 | 186 | type: INTEGER | ||
614 | 187 | name: "pk" | ||
615 | 188 | } | ||
616 | 189 | field_metadata { | ||
617 | 190 | type: INTEGER | ||
618 | 191 | name: "col_int1" | ||
619 | 192 | } | ||
620 | 193 | field_metadata { | ||
621 | 194 | type: INTEGER | ||
622 | 195 | name: "col_int2" | ||
623 | 196 | } | ||
624 | 197 | field_metadata { | ||
625 | 198 | type: INTEGER | ||
626 | 199 | name: "col_int_not_null" | ||
627 | 200 | } | ||
628 | 201 | } | ||
629 | 202 | insert_data { | ||
630 | 203 | segment_id: 1 | ||
631 | 204 | end_segment: true | ||
632 | 205 | record { | ||
633 | 206 | insert_value: "8" | ||
634 | 207 | insert_value: "7" | ||
635 | 208 | insert_value: "7" | ||
636 | 209 | insert_value: "7" | ||
637 | 210 | is_null: false | ||
638 | 211 | is_null: false | ||
639 | 212 | is_null: false | ||
640 | 213 | is_null: false | ||
641 | 214 | } | ||
642 | 215 | } | ||
643 | 216 | } | ||
644 | 217 | |||
645 | 218 | DROP TABLE t1; | ||
646 | 219 | DROP TABLE t2; | ||
647 | 220 | SET GLOBAL transaction_log_truncate_debug= true; | ||
648 | 0 | 221 | ||
649 | === added file 'plugin/transaction_log/tests/t/bug660779-master.opt' | |||
650 | --- plugin/transaction_log/tests/t/bug660779-master.opt 1970-01-01 00:00:00 +0000 | |||
651 | +++ plugin/transaction_log/tests/t/bug660779-master.opt 2010-10-26 14:47:53 +0000 | |||
652 | @@ -0,0 +1,1 @@ | |||
653 | 1 | --transaction-log.enable --scheduler=multi_thread | ||
654 | 0 | 2 | ||
655 | === added file 'plugin/transaction_log/tests/t/bug660779.test' | |||
656 | --- plugin/transaction_log/tests/t/bug660779.test 1970-01-01 00:00:00 +0000 | |||
657 | +++ plugin/transaction_log/tests/t/bug660779.test 2010-10-26 14:47:53 +0000 | |||
658 | @@ -0,0 +1,58 @@ | |||
659 | 1 | CREATE TABLE t1 ( | ||
660 | 2 | pk INT NOT NULL AUTO_INCREMENT, | ||
661 | 3 | col_int1 INT, | ||
662 | 4 | col_int2 INT, | ||
663 | 5 | col_int_not_null INT NOT NULL, | ||
664 | 6 | PRIMARY KEY (pk)); | ||
665 | 7 | |||
666 | 8 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (1,1,1); | ||
667 | 9 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (NULL,1,1); | ||
668 | 10 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (2,1,3); | ||
669 | 11 | |||
670 | 12 | SET GLOBAL transaction_log_truncate_debug= true; | ||
671 | 13 | |||
672 | 14 | --test with no previous Statement message | ||
673 | 15 | BEGIN; | ||
674 | 16 | --ERROR ER_BAD_NULL_ERROR | ||
675 | 17 | UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; | ||
676 | 18 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (5,5,5); | ||
677 | 19 | COMMIT; | ||
678 | 20 | |||
679 | 21 | --echo | ||
680 | 22 | --echo We should have a Transaction with a single insert Statement | ||
681 | 23 | --replace_regex /start_timestamp: [0-9]+/START_TIMESTAMP/g /end_timestamp: [0-9]+/END_TIMESTAMP/g /creation_timestamp: [0-9]+/CREATE_TIMESTAMP/ /update_timestamp: [0-9]+/UPDATE_TIMESTAMP/ | ||
682 | 24 | |||
683 | 25 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
684 | 26 | |||
685 | 27 | BEGIN; | ||
686 | 28 | UPDATE t1 SET col_int1 = (col_int1 + 1) WHERE col_int2 = 1; | ||
687 | 29 | --ERROR ER_BAD_NULL_ERROR | ||
688 | 30 | UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; | ||
689 | 31 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (6,6,6); | ||
690 | 32 | COMMIT; | ||
691 | 33 | |||
692 | 34 | --echo | ||
693 | 35 | --echo We should have a Transaction with 1 update and 1 insert Statement | ||
694 | 36 | --replace_regex /start_timestamp: [0-9]+/START_TIMESTAMP/g /end_timestamp: [0-9]+/END_TIMESTAMP/g /creation_timestamp: [0-9]+/CREATE_TIMESTAMP/ /update_timestamp: [0-9]+/UPDATE_TIMESTAMP/ | ||
695 | 37 | |||
696 | 38 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
697 | 39 | |||
698 | 40 | CREATE TABLE t2 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, a INT); | ||
699 | 41 | INSERT INTO t2 (a) VALUES (1),(2), (NULL); | ||
700 | 42 | |||
701 | 43 | BEGIN; | ||
702 | 44 | --ERROR ER_BAD_NULL_ERROR | ||
703 | 45 | INSERT INTO t1 (col_int_not_null) SELECT a FROM t2; | ||
704 | 46 | INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (7,7,7); | ||
705 | 47 | COMMIT; | ||
706 | 48 | |||
707 | 49 | --echo | ||
708 | 50 | --echo We should have a Transaction with 1 insert Statement | ||
709 | 51 | --replace_regex /start_timestamp: [0-9]+/START_TIMESTAMP/g /end_timestamp: [0-9]+/END_TIMESTAMP/g /creation_timestamp: [0-9]+/CREATE_TIMESTAMP/ /update_timestamp: [0-9]+/UPDATE_TIMESTAMP/ | ||
710 | 52 | |||
711 | 53 | SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); | ||
712 | 54 | |||
713 | 55 | |||
714 | 56 | DROP TABLE t1; | ||
715 | 57 | DROP TABLE t2; | ||
716 | 58 | SET GLOBAL transaction_log_truncate_debug= true; |
Doesn't build on Solaris - http:// hudson. drizzle. org/view/ Drizzle- build/job/ drizzle- build-sparc- solaris10/ 1344/console