1 /* Copyright (c) 2003-2007 MySQL AB
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
16 #include "NDBT_Test.hpp"
17 #include "NDBT_ReturnCodes.h"
18 #include "HugoTransactions.hpp"
19 #include "UtilTransactions.hpp"
20 #include <NdbRestarter.hpp>
22 struct OperationTestCase
{
24 bool preCond
; // start transaction | insert | commit
45 OperationTestCase matrix
[] = {
46 { "ReadRead", true, "READ", 0, 0, "READ", 0, 0, 0, 0 },
47 { "ReadReadEx", true, "READ", 0, 0, "READ-EX", 0, 0, 0, 0 },
48 { "ReadSimpleRead", true, "READ", 0, 0, "S-READ", 0, 0, 0, 0 },
49 { "ReadDirtyRead", true, "READ", 0, 0, "D-READ", 0, 0, 0, 0 },
50 { "ReadInsert", true, "READ", 0, 0, "INSERT", 630, 1, 0, 0 },
51 { "ReadUpdate", true, "READ", 0, 0, "UPDATE", 0, 1, 0, 1 },
52 { "ReadDelete", true, "READ", 0, 0, "DELETE", 0, 0, 626, 0 },
54 { "FReadRead", false, "READ", 626, 0, "READ", 626, 0, 626, 0 },
55 { "FReadReadEx", false, "READ", 626, 0, "READ-EX", 626, 0, 626, 0 },
56 { "FReadSimpleRead", false, "READ", 626, 0, "S-READ", 626, 0, 626, 0 },
57 { "FReadDirtyRead", false, "READ", 626, 0, "D-READ", 626, 0, 626, 0 },
58 { "FReadInsert", false, "READ", 626, 0, "INSERT", 0, 1, 0, 1 },
59 { "FReadUpdate", false, "READ", 626, 0, "UPDATE", 626, 0, 626, 0 },
60 { "FReadDelete", false, "READ", 626, 0, "DELETE", 626, 0, 626, 0 },
62 { "ReadExRead", true, "READ-EX", 0, 0, "READ", 0, 0, 0, 0 },
63 { "ReadExReadEx", true, "READ-EX", 0, 0, "READ-EX", 0, 0, 0, 0 },
64 { "ReadExSimpleRead", true, "READ-EX", 0, 0, "S-READ", 0, 0, 0, 0 },
65 { "ReadExDirtyRead", true, "READ-EX", 0, 0, "D-READ", 0, 0, 0, 0 },
66 { "ReadExInsert", true, "READ-EX", 0, 0, "INSERT", 630, 1, 0, 0 },
67 { "ReadExUpdate", true, "READ-EX", 0, 0, "UPDATE", 0, 1, 0, 1 },
68 { "ReadExDelete", true, "READ-EX", 0, 0, "DELETE", 0, 0, 626, 0 },
70 { "InsertRead", false, "INSERT", 0, 0, "READ", 0, 0, 0, 0 },
71 { "InsertReadEx", false, "INSERT", 0, 0, "READ-EX", 0, 0, 0, 0 },
72 { "InsertSimpleRead",false, "INSERT", 0, 0, "S-READ", 0, 0, 0, 0 },
73 { "InsertDirtyRead", false, "INSERT", 0, 0, "D-READ", 0, 0, 0, 0 },
74 { "InsertInsert", false, "INSERT", 0, 0, "INSERT", 630, 0, 626, 0 },
75 { "InsertUpdate", false, "INSERT", 0, 0, "UPDATE", 0, 1, 0, 1 },
76 { "InsertDelete", false, "INSERT", 0, 0, "DELETE", 0, 0, 626, 0 },
78 { "UpdateRead", true, "UPDATE", 0, 1, "READ", 0, 1, 0, 1 },
79 { "UpdateReadEx", true, "UPDATE", 0, 1, "READ-EX", 0, 1, 0, 1 },
80 { "UpdateSimpleRead", true, "UPDATE", 0, 1, "S-READ", 0, 1, 0, 1 },
81 { "UpdateDirtyRead", true, "UPDATE", 0, 1, "D-READ", 0, 1, 0, 1 },
82 { "UpdateInsert", true, "UPDATE", 0, 1, "INSERT", 630, 0, 0, 0 },
83 { "UpdateUpdate", true, "UPDATE", 0, 1, "UPDATE", 0, 2, 0, 2 },
84 { "UpdateDelete", true, "UPDATE", 0, 1, "DELETE", 0, 0, 626, 0 },
86 { "DeleteRead", true, "DELETE", 0, 0, "READ", 626, 0, 0, 0 },
87 { "DeleteReadEx", true, "DELETE", 0, 0, "READ-EX", 626, 0, 0, 0 },
88 { "DeleteSimpleRead", true, "DELETE", 0, 0, "S-READ", 626, 0, 0, 0 },
89 { "DeleteDirtyRead", true, "DELETE", 0, 0, "D-READ", 626, 0, 626, 0 },
90 { "DeleteInsert", true, "DELETE", 0, 0, "INSERT", 0, 1, 0, 1 },
91 { "DeleteUpdate", true, "DELETE", 0, 0, "UPDATE", 626, 1, 0, 0 },
92 { "DeleteDelete", true, "DELETE", 0, 0, "DELETE", 626, 0, 0, 0 }
95 #define CHECK(b) if (!(b)) { \
96 g_err << "ERR: "<< step->getName() \
97 << " failed on line " << __LINE__ << endl; \
98 result = NDBT_FAILED; \
101 #define C3(b) if (!(b)) { \
102 g_err << "ERR: failed on line " << __LINE__ << endl; \
103 return NDBT_FAILED; }
106 runOp(HugoOperations
& hugoOps
,
111 #define C2(x, y) { int r = (x); int s = (y); if(r != s) {\
112 g_err << "ERR: failed on line " << __LINE__ << ": " \
113 << r << " != " << s << endl; \
114 return NDBT_FAILED; }}
116 if(strcmp(op
, "READ") == 0){
117 C2(hugoOps
.pkReadRecord(pNdb
, 1, 1, NdbOperation::LM_Read
), 0);
118 } else if(strcmp(op
, "READ-EX") == 0){
119 C2(hugoOps
.pkReadRecord(pNdb
, 1, 1, NdbOperation::LM_Exclusive
), 0);
120 } else if(strcmp(op
, "S-READ") == 0){
121 C2(hugoOps
.pkReadRecord(pNdb
, 1, 1, NdbOperation::LM_Read
), 0);
122 } else if(strcmp(op
, "D-READ") == 0){
123 C2(hugoOps
.pkReadRecord(pNdb
, 1, 1, NdbOperation::LM_CommittedRead
), 0);
124 } else if(strcmp(op
, "INSERT") == 0){
125 C2(hugoOps
.pkInsertRecord(pNdb
, 1, 1, value
), 0);
126 } else if(strcmp(op
, "UPDATE") == 0){
127 C2(hugoOps
.pkUpdateRecord(pNdb
, 1, 1, value
), 0);
128 } else if(strcmp(op
, "DELETE") == 0){
129 C2(hugoOps
.pkDeleteRecord(pNdb
, 1, 1), 0);
131 g_err
<< __FILE__
<< " - " << __LINE__
132 << ": Unknown operation" << op
<< endl
;
140 checkVal(HugoOperations
& hugoOps
,
147 if(strcmp(op
, "READ") == 0){
148 } else if(strcmp(op
, "READ-EX") == 0){
149 } else if(strcmp(op
, "S-READ") == 0){
150 } else if(strcmp(op
, "D-READ") == 0){
155 return hugoOps
.verifyUpdatesValue(value
);
159 runTwoOperations(NDBT_Context
* ctx
, NDBT_Step
* step
){
160 int result
= NDBT_OK
;
161 HugoOperations
hugoOps(*ctx
->getTab());
162 Ndb
* pNdb
= GETNDB(step
);
164 const char * op1
= ctx
->getProperty("op1", "NONE");
165 const int val1
= ctx
->getProperty("val1", ~0);
166 const int res1
= ctx
->getProperty("res1", ~0);
167 const char * op2
= ctx
->getProperty("op2", "NONE");
168 const int res2
= ctx
->getProperty("res2", ~0);
169 const int val2
= ctx
->getProperty("val2", ~0);
171 const int res3
= ctx
->getProperty("res3", ~0);
172 const int val3
= ctx
->getProperty("val3", ~0);
176 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
177 CHECK(runOp(hugoOps
, pNdb
, op1
, val1
) == 0);
178 AbortOption oa
= (res1
== 0) ? AbortOnError
: AO_IgnoreError
;
179 CHECK(hugoOps
.execute_NoCommit(pNdb
, oa
) == res1
);
180 CHECK(checkVal(hugoOps
, op1
, val1
, res1
) == 0);
182 ndbout_c("-- running op 2");
184 CHECK(runOp(hugoOps
, pNdb
, op2
, val2
) == 0);
185 CHECK(hugoOps
.execute_Commit(pNdb
) == res2
);
186 CHECK(checkVal(hugoOps
, op2
, val2
, res2
) == 0);
189 hugoOps
.closeTransaction(pNdb
);
191 if(result
!= NDBT_OK
)
195 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
196 CHECK(runOp(hugoOps
, pNdb
, "READ", 0) == 0);
197 CHECK(hugoOps
.execute_Commit(pNdb
) == res3
);
198 CHECK(checkVal(hugoOps
, "READ", val3
, res3
) == 0);
200 hugoOps
.closeTransaction(pNdb
);
206 runInsertRecord(NDBT_Context
* ctx
, NDBT_Step
* step
){
207 int result
= NDBT_OK
;
208 HugoOperations
hugoOps(*ctx
->getTab());
209 Ndb
* pNdb
= GETNDB(step
);
213 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
214 CHECK(hugoOps
.pkInsertRecord(pNdb
, 1) == 0);
215 CHECK(hugoOps
.execute_Commit(pNdb
) == 0);
219 hugoOps
.closeTransaction(pNdb
);
225 runClearTable(NDBT_Context
* ctx
, NDBT_Step
* step
){
226 int records
= ctx
->getNumRecords();
228 UtilTransactions
utilTrans(*ctx
->getTab());
229 if (utilTrans
.clearTable2(GETNDB(step
), records
, 240) != 0){
235 r
.dumpStateAllNodes(&lcp
, 1);
240 enum OPS
{ o_DONE
= 0, o_INS
= 1, o_UPD
= 2, o_DEL
= 3 };
241 typedef Vector
<OPS
> Sequence
;
245 valid(const Sequence
& s
)
250 for(size_t i
= 1; i
<s
.size(); i
++)
270 NdbOut
& operator<<(NdbOut
& out
, const Sequence
& s
)
273 for(size_t i
= 0; i
<s
.size(); i
++)
295 generate(Sequence
& out
, int no
)
299 out
.push_back((OPS
)(no
& 3));
306 generate(Vector
<int>& out
, size_t len
)
316 for(int i
= 0; i
<max
; i
++)
321 if(tmp
.size() >= len
&& valid(tmp
))
328 //ndbout << "DISCARD: " << tmp << endl;
333 static const Uint32 DUMMY
= 0;
334 static const Uint32 ROW
= 1;
337 verify_other(NDBT_Context
* ctx
,
338 Ndb
* pNdb
, int seq
, OPS latest
, bool initial_row
, bool commit
)
340 Uint32 no_wait
= NdbOperation::LM_CommittedRead
*
341 ctx
->getProperty("NoWait", (Uint32
)1);
343 for(size_t j
= no_wait
; j
<3; j
++)
345 HugoOperations
other(*ctx
->getTab());
346 C3(other
.startTransaction(pNdb
) == 0);
347 C3(other
.pkReadRecord(pNdb
, ROW
, 1, (NdbOperation::LockMode
)j
) == 0);
348 int tmp
= other
.execute_Commit(pNdb
);
350 if(j
== NdbOperation::LM_CommittedRead
)
352 C3(initial_row
? tmp
==0 && other
.verifyUpdatesValue(0) == 0 : tmp
==626);
364 C3(tmp
== 0 && other
.verifyUpdatesValue(seq
) == 0);
376 C3(initial_row
? tmp
==0 && other
.verifyUpdatesValue(0) == 0 : tmp
==626);
384 verify_savepoint(NDBT_Context
* ctx
,
385 Ndb
* pNdb
, int seq
, OPS latest
,
386 Uint64 transactionId
)
388 bool initial_row
= (seq
== 0) && latest
== o_INS
;
390 for(size_t j
= 0; j
<3; j
++)
392 const NdbOperation::LockMode lm
= (NdbOperation::LockMode
)j
;
394 HugoOperations
same(*ctx
->getTab());
395 C3(same
.startTransaction(pNdb
) == 0);
396 same
.setTransactionId(transactionId
); // Cheat
399 * Increase savepoint to <em>k</em>
401 for(size_t l
= 1; l
<=seq
; l
++)
403 C3(same
.pkReadRecord(pNdb
, DUMMY
, 1, lm
) == 0); // Read dummy row
404 C3(same
.execute_NoCommit(pNdb
) == 0);
405 g_info
<< "savepoint: " << l
<< endl
;
408 g_info
<< "op(" << seq
<< "): "
409 << " lock mode " << lm
<< endl
;
411 C3(same
.pkReadRecord(pNdb
, ROW
, 1, lm
) == 0); // Read real row
412 int tmp
= same
.execute_Commit(pNdb
);
417 C3(tmp
== 0 && same
.verifyUpdatesValue(0) == 0);
428 C3(tmp
== 0 && same
.verifyUpdatesValue(seq
) == 0);
442 runOperations(NDBT_Context
* ctx
, NDBT_Step
* step
)
445 Ndb
* pNdb
= GETNDB(step
);
447 Uint32 seqNo
= ctx
->getProperty("Sequence", (Uint32
)0);
448 Uint32 commit
= ctx
->getProperty("Commit", (Uint32
)1);
456 generate(seq
, seqNo
);
460 HugoOperations
hugoOps(*ctx
->getTab());
461 C3(hugoOps
.startTransaction(pNdb
) == 0);
462 C3(hugoOps
.pkInsertRecord(pNdb
, DUMMY
, 1, 0) == 0);
463 C3(hugoOps
.execute_Commit(pNdb
) == 0);
466 const bool initial_row
= (seq
[0] != o_INS
);
469 HugoOperations
hugoOps(*ctx
->getTab());
470 C3(hugoOps
.startTransaction(pNdb
) == 0);
471 C3(hugoOps
.pkInsertRecord(pNdb
, ROW
, 1, 0) == 0);
472 C3(hugoOps
.execute_Commit(pNdb
) == 0);
475 HugoOperations
trans1(*ctx
->getTab());
476 C3(trans1
.startTransaction(pNdb
) == 0);
477 for(size_t i
= 0; i
<seq
.size(); i
++)
484 C3(trans1
.pkInsertRecord(pNdb
, ROW
, 1, i
+1) == 0);
487 C3(trans1
.pkUpdateRecord(pNdb
, ROW
, 1, i
+1) == 0);
490 C3(trans1
.pkDeleteRecord(pNdb
, ROW
, 1) == 0);
495 C3(trans1
.execute_NoCommit(pNdb
) == 0);
498 * Verify other transaction
500 if(verify_other(ctx
, pNdb
, 0, seq
[0], initial_row
, commit
) != NDBT_OK
)
504 * Verify savepoint read
506 Uint64 transactionId
= trans1
.getTransaction()->getTransactionId();
508 for(size_t k
=0; k
<=i
+1; k
++)
510 if(verify_savepoint(ctx
, pNdb
, k
,
511 k
>0 ? seq
[k
-1] : initial_row
? o_INS
: o_DONE
,
512 transactionId
) != NDBT_OK
)
519 C3(trans1
.execute_Commit(pNdb
) == 0);
523 C3(trans1
.execute_Rollback(pNdb
) == 0);
526 if(verify_other(ctx
, pNdb
, seq
.size(), seq
.back(),
527 initial_row
, commit
) != NDBT_OK
)
534 runLockUpgrade1(NDBT_Context
* ctx
, NDBT_Step
* step
){
535 // Verify that data in index match
537 Ndb
* pNdb
= GETNDB(step
);
538 HugoOperations
hugoOps(*ctx
->getTab());
539 HugoTransactions
hugoTrans(*ctx
->getTab());
541 if(hugoTrans
.loadTable(pNdb
, 1) != 0){
542 g_err
<< "Load table failed" << endl
;
549 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
550 if(ctx
->getProperty("LOCK_UPGRADE", 1) == 1)
552 CHECK(hugoOps
.pkReadRecord(pNdb
, 0, 1, NdbOperation::LM_Read
) == 0);
553 CHECK(hugoOps
.execute_NoCommit(pNdb
) == 0);
555 ctx
->setProperty("READ_DONE", 1);
558 ctx
->getPropertyWait("READ_DONE", 2);
559 ndbout_c("wait 2 - done");
563 ctx
->setProperty("READ_DONE", 1);
565 ctx
->getPropertyWait("READ_DONE", 2);
566 ndbout_c("wait 2 - done");
567 CHECK(hugoOps
.pkReadRecord(pNdb
, 0, 1, NdbOperation::LM_Read
) == 0);
568 CHECK(hugoOps
.execute_NoCommit(pNdb
) == 0);
570 if(ctx
->getProperty("LU_OP", o_INS
) == o_INS
)
572 CHECK(hugoOps
.pkDeleteRecord(pNdb
, 0, 1) == 0);
573 CHECK(hugoOps
.pkInsertRecord(pNdb
, 0, 1, 2) == 0);
575 else if(ctx
->getProperty("LU_OP", o_UPD
) == o_UPD
)
577 CHECK(hugoOps
.pkUpdateRecord(pNdb
, 0, 1, 2) == 0);
581 CHECK(hugoOps
.pkDeleteRecord(pNdb
, 0, 1) == 0);
583 ctx
->setProperty("READ_DONE", 3);
585 ndbout_c("before update");
586 ndbout_c("wait update");
587 CHECK(hugoOps
.execute_Commit(pNdb
) == 0);
588 CHECK(hugoOps
.closeTransaction(pNdb
) == 0);
590 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
591 CHECK(hugoOps
.pkReadRecord(pNdb
, 0, 1) == 0);
592 int res
= hugoOps
.execute_Commit(pNdb
);
593 if(ctx
->getProperty("LU_OP", o_INS
) == o_INS
)
596 CHECK(hugoOps
.verifyUpdatesValue(2) == 0);
598 else if(ctx
->getProperty("LU_OP", o_UPD
) == o_UPD
)
601 CHECK(hugoOps
.verifyUpdatesValue(2) == 0);
614 runLockUpgrade2(NDBT_Context
* ctx
, NDBT_Step
* step
){
615 // Verify that data in index match
617 Ndb
* pNdb
= GETNDB(step
);
618 HugoOperations
hugoOps(*ctx
->getTab());
619 HugoTransactions
hugoTrans(*ctx
->getTab());
625 CHECK(hugoOps
.startTransaction(pNdb
) == 0);
627 ctx
->getPropertyWait("READ_DONE", 1);
628 ndbout_c("wait 1 - done");
629 CHECK(hugoOps
.pkReadRecord(pNdb
, 0, 1, NdbOperation::LM_Read
) == 0);
630 CHECK(hugoOps
.execute_NoCommit(pNdb
) == 0);
631 ctx
->setProperty("READ_DONE", 2);
634 ctx
->getPropertyWait("READ_DONE", 3);
635 ndbout_c("wait 3 - done");
637 NdbSleep_MilliSleep(200);
638 if(ctx
->getProperty("LU_COMMIT", (Uint32
)0) == 0)
640 CHECK(hugoOps
.execute_Commit(pNdb
) == 0);
644 CHECK(hugoOps
.execute_Rollback(pNdb
) == 0);
652 main(int argc
, const char** argv
){
658 NDBT_TestSuite
ts("testOperations");
660 ts
.setTemporaryTables(true);
662 for(Uint32 i
= 0; i
< 12; i
++)
664 if(false && (i
== 6 || i
== 8 || i
== 10))
667 BaseString
name("bug_9749");
668 name
.appfmt("_%d", i
);
669 NDBT_TestCaseImpl1
*pt
= new NDBT_TestCaseImpl1(&ts
,
672 pt
->setProperty("LOCK_UPGRADE", 1 + (i
& 1));
673 pt
->setProperty("LU_OP", 1 + ((i
>> 1) % 3));
674 pt
->setProperty("LU_COMMIT", i
/ 6);
676 pt
->addInitializer(new NDBT_Initializer(pt
,
680 pt
->addStep(new NDBT_ParallelStep(pt
,
685 pt
->addStep(new NDBT_ParallelStep(pt
,
689 pt
->addFinalizer(new NDBT_Finalizer(pt
,
695 for(size_t i
= 0; i
<tmp
.size(); i
++)
700 for(size_t j
= 0; j
<s
.size(); j
++){
718 n1
.append("_COMMIT");
720 NDBT_TestCaseImpl1
*pt
= new NDBT_TestCaseImpl1(&ts
,
723 pt
->setProperty("Sequence", tmp
[i
]);
724 pt
->addInitializer(new NDBT_Initializer(pt
,
728 pt
->addStep(new NDBT_ParallelStep(pt
,
732 pt
->addFinalizer(new NDBT_Finalizer(pt
,
738 name
.append("_ABORT");
739 pt
= new NDBT_TestCaseImpl1(&ts
, name
.c_str()+1, "");
740 pt
->setProperty("Sequence", tmp
[i
]);
741 pt
->setProperty("Commit", (Uint32
)0);
742 pt
->addInitializer(new NDBT_Initializer(pt
,
746 pt
->addStep(new NDBT_ParallelStep(pt
,
750 pt
->addFinalizer(new NDBT_Finalizer(pt
,
757 for(Uint32 i
= 0; i
<sizeof(matrix
)/sizeof(matrix
[0]); i
++){
758 NDBT_TestCaseImpl1
*pt
= new NDBT_TestCaseImpl1(&ts
, matrix
[i
].name
, "");
760 pt
->addInitializer(new NDBT_Initializer(pt
,
764 if(matrix
[i
].preCond
){
765 pt
->addInitializer(new NDBT_Initializer(pt
,
770 pt
->setProperty("op1", matrix
[i
].op1
);
771 pt
->setProperty("res1", matrix
[i
].res1
);
772 pt
->setProperty("val1", matrix
[i
].val1
);
774 pt
->setProperty("op2", matrix
[i
].op2
);
775 pt
->setProperty("res2", matrix
[i
].res2
);
776 pt
->setProperty("val2", matrix
[i
].val2
);
778 pt
->setProperty("res3", matrix
[i
].res3
);
779 pt
->setProperty("val3", matrix
[i
].val3
);
781 pt
->addStep(new NDBT_ParallelStep(pt
,
784 pt
->addFinalizer(new NDBT_Finalizer(pt
,
791 return ts
.execute(argc
, argv
);
794 template class Vector
<OPS
>;
795 template class Vector
<Sequence
>;