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 | */ |
6 | |
7 | #include "config.h" |
8 | -#include <drizzled/session.h> |
9 | +#include "drizzled/session.h" |
10 | #include "drizzled/session_list.h" |
11 | #include <sys/stat.h> |
12 | -#include <drizzled/error.h> |
13 | -#include <drizzled/gettext.h> |
14 | -#include <drizzled/query_id.h> |
15 | -#include <drizzled/data_home.h> |
16 | -#include <drizzled/sql_base.h> |
17 | -#include <drizzled/lock.h> |
18 | -#include <drizzled/item/cache.h> |
19 | -#include <drizzled/item/float.h> |
20 | -#include <drizzled/item/return_int.h> |
21 | -#include <drizzled/item/empty_string.h> |
22 | -#include <drizzled/show.h> |
23 | -#include <drizzled/plugin/client.h> |
24 | +#include "drizzled/error.h" |
25 | +#include "drizzled/gettext.h" |
26 | +#include "drizzled/query_id.h" |
27 | +#include "drizzled/data_home.h" |
28 | +#include "drizzled/sql_base.h" |
29 | +#include "drizzled/lock.h" |
30 | +#include "drizzled/item/cache.h" |
31 | +#include "drizzled/item/float.h" |
32 | +#include "drizzled/item/return_int.h" |
33 | +#include "drizzled/item/empty_string.h" |
34 | +#include "drizzled/show.h" |
35 | +#include "drizzled/plugin/client.h" |
36 | #include "drizzled/plugin/scheduler.h" |
37 | #include "drizzled/plugin/authentication.h" |
38 | #include "drizzled/plugin/logging.h" |
39 | @@ -60,7 +60,7 @@ |
40 | #include <fcntl.h> |
41 | #include <algorithm> |
42 | #include <climits> |
43 | -#include "boost/filesystem.hpp" |
44 | +#include <boost/filesystem.hpp> |
45 | |
46 | using namespace std; |
47 | |
48 | |
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 | #ifndef DRIZZLED_SESSION_H |
54 | #define DRIZZLED_SESSION_H |
55 | |
56 | -/* Classes in mysql */ |
57 | - |
58 | #include "drizzled/plugin.h" |
59 | -#include <drizzled/sql_locale.h> |
60 | +#include "drizzled/sql_locale.h" |
61 | #include "drizzled/resource_context.h" |
62 | -#include <drizzled/cursor.h> |
63 | -#include <drizzled/current_session.h> |
64 | -#include <drizzled/sql_error.h> |
65 | -#include <drizzled/file_exchange.h> |
66 | -#include <drizzled/select_result_interceptor.h> |
67 | -#include <drizzled/statistics_variables.h> |
68 | -#include <drizzled/xid.h> |
69 | +#include "drizzled/cursor.h" |
70 | +#include "drizzled/current_session.h" |
71 | +#include "drizzled/sql_error.h" |
72 | +#include "drizzled/file_exchange.h" |
73 | +#include "drizzled/select_result_interceptor.h" |
74 | +#include "drizzled/statistics_variables.h" |
75 | +#include "drizzled/xid.h" |
76 | #include "drizzled/query_id.h" |
77 | #include "drizzled/named_savepoint.h" |
78 | #include "drizzled/transaction_context.h" |
79 | #include "drizzled/util/storable.h" |
80 | - |
81 | #include "drizzled/my_hash.h" |
82 | |
83 | #include <netdb.h> |
84 | @@ -47,14 +44,11 @@ |
85 | #include <deque> |
86 | |
87 | #include "drizzled/internal/getrusage.h" |
88 | - |
89 | -#include <drizzled/security_context.h> |
90 | -#include <drizzled/open_tables_state.h> |
91 | - |
92 | -#include <drizzled/internal_error_handler.h> |
93 | -#include <drizzled/diagnostics_area.h> |
94 | - |
95 | -#include <drizzled/plugin/authorization.h> |
96 | +#include "drizzled/security_context.h" |
97 | +#include "drizzled/open_tables_state.h" |
98 | +#include "drizzled/internal_error_handler.h" |
99 | +#include "drizzled/diagnostics_area.h" |
100 | +#include "drizzled/plugin/authorization.h" |
101 | |
102 | #include <boost/unordered_map.hpp> |
103 | #include <boost/thread/mutex.hpp> |
104 | @@ -550,10 +544,38 @@ |
105 | ResourceContext *getResourceContext(const plugin::MonitoredInTransaction *monitored, |
106 | size_t index= 0); |
107 | |
108 | + /** |
109 | + * Structure used to manage "statement transactions" and |
110 | + * "normal transactions". In autocommit mode, the normal transaction is |
111 | + * equivalent to the statement transaction. |
112 | + * |
113 | + * Storage engines will be registered here when they participate in |
114 | + * a transaction. No engine is registered more than once. |
115 | + */ |
116 | struct st_transactions { |
117 | std::deque<NamedSavepoint> savepoints; |
118 | - TransactionContext all; ///< Trans since BEGIN WORK |
119 | - TransactionContext stmt; ///< Trans for current statement |
120 | + |
121 | + /** |
122 | + * The normal transaction (since BEGIN WORK). |
123 | + * |
124 | + * Contains a list of all engines that have participated in any of the |
125 | + * statement transactions started within the context of the normal |
126 | + * transaction. |
127 | + * |
128 | + * @note In autocommit mode, this is empty. |
129 | + */ |
130 | + TransactionContext all; |
131 | + |
132 | + /** |
133 | + * The statment transaction. |
134 | + * |
135 | + * Contains a list of all engines participating in the given statement. |
136 | + * |
137 | + * @note In autocommit mode, this will be used to commit/rollback the |
138 | + * normal transaction. |
139 | + */ |
140 | + TransactionContext stmt; |
141 | + |
142 | XID_STATE xid_state; |
143 | |
144 | void cleanup() |
145 | @@ -1197,9 +1219,8 @@ |
146 | * Current implementation does not depend on that, but future changes |
147 | * should be done with this in mind; |
148 | * |
149 | - * @param Scrambled password received from client |
150 | - * @param Length of scrambled password |
151 | - * @param Database name to connect to, may be NULL |
152 | + * @param passwd Scrambled password received from client |
153 | + * @param db Database name to connect to, may be NULL |
154 | */ |
155 | bool checkUser(const std::string &passwd, const std::string &db); |
156 | |
157 | |
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 | store_values(values); |
163 | session->count_cuted_fields= CHECK_FIELD_IGNORE; |
164 | if (session->is_error()) |
165 | + { |
166 | + /* |
167 | + * If we fail mid-way through INSERT..SELECT, we need to remove any |
168 | + * records that we added to the current Statement message. We can |
169 | + * use session->row_count to know how many records we have already added. |
170 | + */ |
171 | + TransactionServices &ts= TransactionServices::singleton(); |
172 | + ts.removeStatementRecords(session, (session->row_count - 1)); |
173 | return(1); |
174 | + } |
175 | |
176 | // Release latches in case bulk insert takes a long time |
177 | plugin::TransactionalStorageEngine::releaseTemporaryLatches(session); |
178 | |
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 | #include "drizzled/records.h" |
184 | #include "drizzled/internal/my_sys.h" |
185 | #include "drizzled/internal/iocache.h" |
186 | +#include "drizzled/transaction_services.h" |
187 | |
188 | #include <boost/dynamic_bitset.hpp> |
189 | #include <list> |
190 | @@ -473,7 +474,20 @@ |
191 | |
192 | table->storeRecord(); |
193 | if (fill_record(session, fields, values)) |
194 | + { |
195 | + /* |
196 | + * If we updated some rows before this one failed (updated > 0), |
197 | + * then we will need to undo adding those records to the |
198 | + * replication Statement message. |
199 | + */ |
200 | + if (updated > 0) |
201 | + { |
202 | + TransactionServices &ts= TransactionServices::singleton(); |
203 | + ts.removeStatementRecords(session, updated); |
204 | + } |
205 | + |
206 | break; |
207 | + } |
208 | |
209 | found++; |
210 | |
211 | @@ -486,15 +500,15 @@ |
212 | table->auto_increment_field_not_null= false; |
213 | |
214 | if (!error || error == HA_ERR_RECORD_IS_THE_SAME) |
215 | - { |
216 | + { |
217 | if (error != HA_ERR_RECORD_IS_THE_SAME) |
218 | updated++; |
219 | else |
220 | error= 0; |
221 | - } |
222 | - else if (! ignore || |
223 | + } |
224 | + else if (! ignore || |
225 | table->cursor->is_fatal_error(error, HA_CHECK_DUP_KEY)) |
226 | - { |
227 | + { |
228 | /* |
229 | If (ignore && error is ignorable) we don't have to |
230 | do anything; otherwise... |
231 | @@ -505,10 +519,10 @@ |
232 | flags|= ME_FATALERROR; /* Other handler errors are fatal */ |
233 | |
234 | prepare_record_for_error_message(error, table); |
235 | - table->print_error(error,MYF(flags)); |
236 | - error= 1; |
237 | - break; |
238 | - } |
239 | + table->print_error(error,MYF(flags)); |
240 | + error= 1; |
241 | + break; |
242 | + } |
243 | } |
244 | |
245 | if (!--limit && using_limit) |
246 | |
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 | #include "drizzled/plugin/xa_resource_manager.h" |
252 | #include "drizzled/internal/my_sys.h" |
253 | |
254 | -using namespace std; |
255 | - |
256 | #include <vector> |
257 | #include <algorithm> |
258 | #include <functional> |
259 | +#include <google/protobuf/repeated_field.h> |
260 | + |
261 | +using namespace std; |
262 | +using namespace google; |
263 | |
264 | namespace drizzled |
265 | { |
266 | @@ -1826,6 +1828,123 @@ |
267 | } |
268 | } |
269 | |
270 | + |
271 | +/** |
272 | + * Template for removing Statement records of different types. |
273 | + * |
274 | + * The code for removing records from different Statement message types |
275 | + * is identical except for the class types that are embedded within the |
276 | + * Statement. |
277 | + * |
278 | + * There are 3 scenarios we need to look for: |
279 | + * - We've been asked to remove more records than exist in the Statement |
280 | + * - We've been asked to remove less records than exist in the Statement |
281 | + * - We've been asked to remove ALL records that exist in the Statement |
282 | + * |
283 | + * If we are removing ALL records, then effectively we would be left with |
284 | + * an empty Statement message, so we should just remove it and clean up |
285 | + * message pointers in the Session object. |
286 | + */ |
287 | +template <class DataType, class RecordType> |
288 | +static bool removeStatementRecordsWithType(Session *session, |
289 | + DataType *data, |
290 | + uint32_t count) |
291 | +{ |
292 | + uint32_t num_avail_recs= static_cast<uint32_t>(data->record_size()); |
293 | + |
294 | + /* If there aren't enough records to remove 'count' of them, error. */ |
295 | + if (num_avail_recs < count) |
296 | + return false; |
297 | + |
298 | + /* |
299 | + * If we are removing all of the data records, we'll just remove this |
300 | + * entire Statement message. |
301 | + */ |
302 | + if (num_avail_recs == count) |
303 | + { |
304 | + message::Transaction *transaction= session->getTransactionMessage(); |
305 | + protobuf::RepeatedPtrField<message::Statement> *statements= transaction->mutable_statement(); |
306 | + statements->RemoveLast(); |
307 | + |
308 | + /* |
309 | + * Now need to set the Session Statement pointer to either the previous |
310 | + * Statement, or NULL if there isn't one. |
311 | + */ |
312 | + if (statements->size() == 0) |
313 | + { |
314 | + session->setStatementMessage(NULL); |
315 | + } |
316 | + else |
317 | + { |
318 | + /* |
319 | + * There isn't a great way to get a pointer to the previous Statement |
320 | + * message using the RepeatedPtrField object, so we'll just get to it |
321 | + * using the Transaction message. |
322 | + */ |
323 | + int last_stmt_idx= transaction->statement_size() - 1; |
324 | + session->setStatementMessage(transaction->mutable_statement(last_stmt_idx)); |
325 | + } |
326 | + } |
327 | + /* We only need to remove 'count' records */ |
328 | + else if (num_avail_recs > count) |
329 | + { |
330 | + protobuf::RepeatedPtrField<RecordType> *records= data->mutable_record(); |
331 | + while (count--) |
332 | + records->RemoveLast(); |
333 | + } |
334 | + |
335 | + return true; |
336 | +} |
337 | + |
338 | + |
339 | +bool TransactionServices::removeStatementRecords(Session *session, |
340 | + uint32_t count) |
341 | +{ |
342 | + ReplicationServices &replication_services= ReplicationServices::singleton(); |
343 | + if (! replication_services.isActive()) |
344 | + return false; |
345 | + |
346 | + /* Get the most current Statement */ |
347 | + message::Statement *statement= session->getStatementMessage(); |
348 | + |
349 | + /* Make sure we have work to do */ |
350 | + if (statement == NULL) |
351 | + return false; |
352 | + |
353 | + bool retval= false; |
354 | + |
355 | + switch (statement->type()) |
356 | + { |
357 | + case message::Statement::INSERT: |
358 | + { |
359 | + message::InsertData *data= statement->mutable_insert_data(); |
360 | + retval= removeStatementRecordsWithType<message::InsertData, message::InsertRecord>(session, data, count); |
361 | + break; |
362 | + } |
363 | + |
364 | + case message::Statement::UPDATE: |
365 | + { |
366 | + message::UpdateData *data= statement->mutable_update_data(); |
367 | + retval= removeStatementRecordsWithType<message::UpdateData, message::UpdateRecord>(session, data, count); |
368 | + break; |
369 | + } |
370 | + |
371 | + case message::Statement::DELETE: /* not sure if this one is possible... */ |
372 | + { |
373 | + message::DeleteData *data= statement->mutable_delete_data(); |
374 | + retval= removeStatementRecordsWithType<message::DeleteData, message::DeleteRecord>(session, data, count); |
375 | + break; |
376 | + } |
377 | + |
378 | + default: |
379 | + retval= false; |
380 | + break; |
381 | + } |
382 | + |
383 | + return retval; |
384 | +} |
385 | + |
386 | + |
387 | void TransactionServices::createTable(Session *in_session, |
388 | const message::Table &table) |
389 | { |
390 | |
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 | * @param use_update_record If true, uses the values from the update row instead |
396 | */ |
397 | void deleteRecord(Session *in_session, Table *in_table, bool use_update_record= false); |
398 | + |
399 | + /** |
400 | + * Used to undo effects of a failed statement. |
401 | + * |
402 | + * An SQL statement, like an UPDATE, that affects multiple rows could |
403 | + * potentially fail mid-way through processing the rows. In such a case, |
404 | + * the successfully modified rows that preceeded the failing row would |
405 | + * have been added to the Statement message. This method is used for |
406 | + * rolling back that change. |
407 | + * |
408 | + * @note |
409 | + * This particular failure is seen on column constraint violations |
410 | + * during a multi-row UPDATE and a multi-row INSERT..SELECT. |
411 | + * |
412 | + * @param in_session Pointer to the Session containing the Statement |
413 | + * @param count The number of records to remove from Statement. |
414 | + * |
415 | + * @retval true Successfully removed 'count' records |
416 | + * @retval false Failure |
417 | + */ |
418 | + bool removeStatementRecords(Session *in_session, uint32_t count); |
419 | + |
420 | /** |
421 | * Creates a CreateSchema Statement GPB message and adds it |
422 | * to the Session's active Transaction GPB message for pushing |
423 | |
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 | +CREATE TABLE t1 ( |
429 | +pk INT NOT NULL AUTO_INCREMENT, |
430 | +col_int1 INT, |
431 | +col_int2 INT, |
432 | +col_int_not_null INT NOT NULL, |
433 | +PRIMARY KEY (pk)); |
434 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (1,1,1); |
435 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (NULL,1,1); |
436 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (2,1,3); |
437 | +SET GLOBAL transaction_log_truncate_debug= true; |
438 | +BEGIN; |
439 | +UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; |
440 | +ERROR 23000: Column 'col_int_not_null' cannot be null |
441 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (5,5,5); |
442 | +COMMIT; |
443 | + |
444 | +We should have a Transaction with a single insert Statement |
445 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
446 | +PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) |
447 | +transaction_context { |
448 | + server_id: 1 |
449 | + transaction_id: 1 |
450 | + START_TIMESTAMP |
451 | + END_TIMESTAMP |
452 | +} |
453 | +statement { |
454 | + type: INSERT |
455 | + START_TIMESTAMP |
456 | + END_TIMESTAMP |
457 | + insert_header { |
458 | + table_metadata { |
459 | + schema_name: "test" |
460 | + table_name: "t1" |
461 | + } |
462 | + field_metadata { |
463 | + type: INTEGER |
464 | + name: "pk" |
465 | + } |
466 | + field_metadata { |
467 | + type: INTEGER |
468 | + name: "col_int1" |
469 | + } |
470 | + field_metadata { |
471 | + type: INTEGER |
472 | + name: "col_int2" |
473 | + } |
474 | + field_metadata { |
475 | + type: INTEGER |
476 | + name: "col_int_not_null" |
477 | + } |
478 | + } |
479 | + insert_data { |
480 | + segment_id: 1 |
481 | + end_segment: true |
482 | + record { |
483 | + insert_value: "4" |
484 | + insert_value: "5" |
485 | + insert_value: "5" |
486 | + insert_value: "5" |
487 | + is_null: false |
488 | + is_null: false |
489 | + is_null: false |
490 | + is_null: false |
491 | + } |
492 | + } |
493 | +} |
494 | + |
495 | +BEGIN; |
496 | +UPDATE t1 SET col_int1 = (col_int1 + 1) WHERE col_int2 = 1; |
497 | +UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; |
498 | +ERROR 23000: Column 'col_int_not_null' cannot be null |
499 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (6,6,6); |
500 | +COMMIT; |
501 | + |
502 | +We should have a Transaction with 1 update and 1 insert Statement |
503 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
504 | +PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) |
505 | +transaction_context { |
506 | + server_id: 1 |
507 | + transaction_id: 2 |
508 | + START_TIMESTAMP |
509 | + END_TIMESTAMP |
510 | +} |
511 | +statement { |
512 | + type: UPDATE |
513 | + START_TIMESTAMP |
514 | + END_TIMESTAMP |
515 | + update_header { |
516 | + table_metadata { |
517 | + schema_name: "test" |
518 | + table_name: "t1" |
519 | + } |
520 | + key_field_metadata { |
521 | + type: INTEGER |
522 | + name: "pk" |
523 | + } |
524 | + set_field_metadata { |
525 | + type: INTEGER |
526 | + name: "col_int1" |
527 | + } |
528 | + } |
529 | + update_data { |
530 | + segment_id: 1 |
531 | + end_segment: true |
532 | + record { |
533 | + key_value: "1" |
534 | + after_value: "2" |
535 | + is_null: false |
536 | + } |
537 | + record { |
538 | + key_value: "3" |
539 | + after_value: "3" |
540 | + is_null: false |
541 | + } |
542 | + } |
543 | +} |
544 | +statement { |
545 | + type: INSERT |
546 | + START_TIMESTAMP |
547 | + END_TIMESTAMP |
548 | + insert_header { |
549 | + table_metadata { |
550 | + schema_name: "test" |
551 | + table_name: "t1" |
552 | + } |
553 | + field_metadata { |
554 | + type: INTEGER |
555 | + name: "pk" |
556 | + } |
557 | + field_metadata { |
558 | + type: INTEGER |
559 | + name: "col_int1" |
560 | + } |
561 | + field_metadata { |
562 | + type: INTEGER |
563 | + name: "col_int2" |
564 | + } |
565 | + field_metadata { |
566 | + type: INTEGER |
567 | + name: "col_int_not_null" |
568 | + } |
569 | + } |
570 | + insert_data { |
571 | + segment_id: 1 |
572 | + end_segment: true |
573 | + record { |
574 | + insert_value: "5" |
575 | + insert_value: "6" |
576 | + insert_value: "6" |
577 | + insert_value: "6" |
578 | + is_null: false |
579 | + is_null: false |
580 | + is_null: false |
581 | + is_null: false |
582 | + } |
583 | + } |
584 | +} |
585 | + |
586 | +CREATE TABLE t2 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, a INT); |
587 | +INSERT INTO t2 (a) VALUES (1),(2), (NULL); |
588 | +BEGIN; |
589 | +INSERT INTO t1 (col_int_not_null) SELECT a FROM t2; |
590 | +ERROR 23000: Column 'col_int_not_null' cannot be null |
591 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (7,7,7); |
592 | +COMMIT; |
593 | + |
594 | +We should have a Transaction with 1 insert Statement |
595 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
596 | +PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)) |
597 | +transaction_context { |
598 | + server_id: 1 |
599 | + transaction_id: 5 |
600 | + START_TIMESTAMP |
601 | + END_TIMESTAMP |
602 | +} |
603 | +statement { |
604 | + type: INSERT |
605 | + START_TIMESTAMP |
606 | + END_TIMESTAMP |
607 | + insert_header { |
608 | + table_metadata { |
609 | + schema_name: "test" |
610 | + table_name: "t1" |
611 | + } |
612 | + field_metadata { |
613 | + type: INTEGER |
614 | + name: "pk" |
615 | + } |
616 | + field_metadata { |
617 | + type: INTEGER |
618 | + name: "col_int1" |
619 | + } |
620 | + field_metadata { |
621 | + type: INTEGER |
622 | + name: "col_int2" |
623 | + } |
624 | + field_metadata { |
625 | + type: INTEGER |
626 | + name: "col_int_not_null" |
627 | + } |
628 | + } |
629 | + insert_data { |
630 | + segment_id: 1 |
631 | + end_segment: true |
632 | + record { |
633 | + insert_value: "8" |
634 | + insert_value: "7" |
635 | + insert_value: "7" |
636 | + insert_value: "7" |
637 | + is_null: false |
638 | + is_null: false |
639 | + is_null: false |
640 | + is_null: false |
641 | + } |
642 | + } |
643 | +} |
644 | + |
645 | +DROP TABLE t1; |
646 | +DROP TABLE t2; |
647 | +SET GLOBAL transaction_log_truncate_debug= true; |
648 | |
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 | +--transaction-log.enable --scheduler=multi_thread |
654 | |
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 | +CREATE TABLE t1 ( |
660 | + pk INT NOT NULL AUTO_INCREMENT, |
661 | + col_int1 INT, |
662 | + col_int2 INT, |
663 | + col_int_not_null INT NOT NULL, |
664 | + PRIMARY KEY (pk)); |
665 | + |
666 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (1,1,1); |
667 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (NULL,1,1); |
668 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (2,1,3); |
669 | + |
670 | +SET GLOBAL transaction_log_truncate_debug= true; |
671 | + |
672 | +--test with no previous Statement message |
673 | +BEGIN; |
674 | +--ERROR ER_BAD_NULL_ERROR |
675 | +UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; |
676 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (5,5,5); |
677 | +COMMIT; |
678 | + |
679 | +--echo |
680 | +--echo We should have a Transaction with a single insert Statement |
681 | +--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 | + |
683 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
684 | + |
685 | +BEGIN; |
686 | +UPDATE t1 SET col_int1 = (col_int1 + 1) WHERE col_int2 = 1; |
687 | +--ERROR ER_BAD_NULL_ERROR |
688 | +UPDATE t1 SET col_int_not_null = col_int1 WHERE col_int2 = 1; |
689 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (6,6,6); |
690 | +COMMIT; |
691 | + |
692 | +--echo |
693 | +--echo We should have a Transaction with 1 update and 1 insert Statement |
694 | +--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 | + |
696 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
697 | + |
698 | +CREATE TABLE t2 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, a INT); |
699 | +INSERT INTO t2 (a) VALUES (1),(2), (NULL); |
700 | + |
701 | +BEGIN; |
702 | +--ERROR ER_BAD_NULL_ERROR |
703 | +INSERT INTO t1 (col_int_not_null) SELECT a FROM t2; |
704 | +INSERT INTO t1 (col_int1, col_int2, col_int_not_null) VALUES (7,7,7); |
705 | +COMMIT; |
706 | + |
707 | +--echo |
708 | +--echo We should have a Transaction with 1 insert Statement |
709 | +--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 | + |
711 | +SELECT PRINT_TRANSACTION_MESSAGE('transaction.log',(select max(entry_offset) from DATA_DICTIONARY.TRANSACTION_LOG_TRANSACTIONS)); |
712 | + |
713 | + |
714 | +DROP TABLE t1; |
715 | +DROP TABLE t2; |
716 | +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