1 /*
2 Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <ndb_global.h>
26 #include <ndb_opts.h>
27 #include <NdbApi.hpp>
28 #include <NdbTest.hpp>
29 #include <ndb_version.h>
30
31 #include <ndb_rand.h>
32
33 // version >= 5.1 required
34
35 #if !defined(min) || !defined(max)
36 #define min(x, y) ((x) < (y) ? (x) : (y))
37 #define max(x, y) ((x) > (y) ? (x) : (y))
38 #endif
39
40 /*
41 * Test composite operations on same PK via events. The merge of event
42 * data can happen in 2 places:
43 *
44 * 1) In TUP at commit, the detached triggers report a single composite
45 * operation and its post/pre data
46 *
47 * 2) In event API version >= 5.1 separate commits within same GCI are
48 * optionally merged. This is required to read blob data via NdbBlob.
49 *
50 * In this test merge is on by default.
51 *
52 * Option --separate-events disables GCI merge and implies --no-blobs.
53 * This is used to test basic events functionality.
54 *
55 * Option --no-blobs omits blob attributes. This is used to test GCI
56 * merge without getting into blob bugs.
57 *
58 * Option --no-multiops allows 1 operation per commit. This avoids TUP
59 * and blob multi-operation bugs.
60 *
61 * There are other -no-* options, each added to isolate a specific bug.
62 *
63 * There are 5 ways (ignoring NUL operand) to compose 2 ops:
64 *
65 * INS o DEL = NUL
66 * INS o UPD = INS
67 * DEL o INS = UPD
68 * UPD o DEL = DEL
69 * UPD o UPD = UPD
70 *
71 * Event merge in NDB API handles idempotent INS o INS and DEL o DEL
72 * which are possible on NF (node failure). This test does not handle
73 * them when --separate-events is used.
74 */
75
76 struct Opts {
77 my_bool abort_on_error;
78 int blob_version;
79 int loglevel;
80 uint loop;
81 uint maxops;
82 uint maxpk;
83 my_bool no_blobs;
84 my_bool no_implicit_nulls;
85 my_bool no_missing_update;
86 my_bool no_multiops;
87 my_bool no_nulls;
88 my_bool one_blob;
89 const char* opstring;
90 uint seed;
91 int maxtab;
92 my_bool separate_events;
93 uint tweak; // whatever's useful
94 my_bool use_table;
95 };
96
97 static Opts g_opts;
98 static const uint g_maxpk = 1000;
99 static const uint g_maxtab = 100;
100 static const uint g_maxopstringpart = 100;
101 static const char* g_opstringpart[g_maxopstringpart];
102 static uint g_opstringparts = 0;
103 static uint g_loop = 0;
104
105 static Ndb_cluster_connection* g_ncc = 0;
106 static Ndb* g_ndb = 0;
107 static NdbDictionary::Dictionary* g_dic = 0;
108 static NdbTransaction* g_con = 0;
109 static NdbOperation* g_op = 0;
110 static NdbScanOperation* g_scan_op = 0;
111
112 static const uint g_charlen = 5;
113 static const char* g_charval = "abcdefgh";
114 static const char* g_csname = "latin1_swedish_ci";
115
116 static uint g_blobinlinesize = 256;
117 static uint g_blobpartsize = 2000;
118 static const uint g_maxblobsize = 100000;
119
120 static NdbEventOperation* g_evt_op = 0;
121 static NdbBlob* g_bh = 0;
122
123 static uint
urandom()124 urandom()
125 {
126 uint r = (uint)ndb_rand();
127 return r;
128 }
129
130 static uint
urandom(uint m)131 urandom(uint m)
132 {
133 if (m == 0)
134 return 0;
135 uint r = urandom();
136 r = r % m;
137 return r;
138 }
139
140 static bool
urandom(uint per,uint cent)141 urandom(uint per, uint cent)
142 {
143 return urandom(cent) < per;
144 }
145
146 static int& g_loglevel = g_opts.loglevel; // default log level
147
148 #define chkdb(x) \
149 do { if (x) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; errdb(); if (g_opts.abort_on_error) abort(); return -1; } while (0)
150
151 #define chkrc(x) \
152 do { if (x) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; if (g_opts.abort_on_error) abort(); return -1; } while (0)
153
154 #define reqrc(x) \
155 do { if (x) break; ndbout << "line " << __LINE__ << " ASSERT " << #x << endl; abort(); } while (0)
156
157 #define ll0(x) \
158 do { if (g_loglevel < 0) break; ndbout << x << endl; } while (0)
159
160 #define ll1(x) \
161 do { if (g_loglevel < 1) break; ndbout << x << endl; } while (0)
162
163 #define ll2(x) \
164 do { if (g_loglevel < 2) break; ndbout << x << endl; } while (0)
165
166 #define ll3(x) \
167 do { if (g_loglevel < 3) break; ndbout << x << endl; } while (0)
168
169 static void
errdb()170 errdb()
171 {
172 uint any = 0;
173 // g_ncc return no error...
174 if (g_ndb != 0) {
175 const NdbError& e = g_ndb->getNdbError();
176 if (e.code != 0)
177 ll0(++any << " ndb: error " << e);
178 }
179 if (g_dic != 0) {
180 const NdbError& e = g_dic->getNdbError();
181 if (e.code != 0)
182 ll0(++any << " dic: error " << e);
183 }
184 if (g_con != 0) {
185 const NdbError& e = g_con->getNdbError();
186 if (e.code != 0)
187 ll0(++any << " con: error " << e);
188 }
189 if (g_op != 0) {
190 const NdbError& e = g_op->getNdbError();
191 if (e.code != 0)
192 ll0(++any << " op: error " << e);
193 }
194 if (g_scan_op != 0) {
195 const NdbError& e = g_scan_op->getNdbError();
196 if (e.code != 0)
197 ll0(++any << " scan_op: error " << e);
198 }
199 if (g_evt_op != 0) {
200 const NdbError& e = g_evt_op->getNdbError();
201 if (e.code != 0)
202 ll0(++any << " evt_op: error " << e);
203 }
204 if (g_bh != 0) {
205 const NdbError& e = g_bh->getNdbError();
206 if (e.code != 0)
207 ll0(++any << " bh: error " << e);
208 }
209 if (! any)
210 ll0("unknown db error");
211 }
212
213 struct Col {
214 uint no;
215 const char* name;
216 NdbDictionary::Column::Type type;
217 bool pk;
218 bool nullable;
219 uint length;
220 uint size;
221 uint inlinesize;
222 uint partsize;
223 uint stripesize;
isblobCol224 bool isblob() const {
225 return
226 type == NdbDictionary::Column::Text ||
227 type == NdbDictionary::Column::Blob;
228 }
229 };
230
231 // put var* pk first
232 static const Col g_col[] = {
233 { 0, "pk2", NdbDictionary::Column::Varchar,
234 true, false,
235 g_charlen, 1 + g_charlen, 0, 0, 0 },
236 { 1, "seq", NdbDictionary::Column::Unsigned,
237 false, true,
238 1, 4, 0, 0, 0 },
239 { 2, "pk1", NdbDictionary::Column::Unsigned,
240 true, false,
241 1, 4, 0, 0, 0 },
242 { 3, "cc1", NdbDictionary::Column::Char,
243 false, true,
244 g_charlen, g_charlen, 0, 0, 0 },
245 { 4, "tx1", NdbDictionary::Column::Text,
246 false, true,
247 0, 0, g_blobinlinesize, g_blobpartsize, 0 }, // V2 distribution
248 { 5, "tx2", NdbDictionary::Column::Text,
249 false, true,
250 0, 0, g_blobinlinesize, g_blobpartsize, 4 },
251 { 6, "bl1", NdbDictionary::Column::Blob, // tinyblob
252 false, true,
253 0, 0, g_blobinlinesize, 0, 0 }
254 };
255
256 static const uint g_maxcol = sizeof(g_col)/sizeof(g_col[0]);
257 static const uint g_blobcols = 3;
258
259 static uint
ncol()260 ncol()
261 {
262 uint n = g_maxcol;
263 if (g_opts.no_blobs)
264 n -= g_blobcols;
265 else if (g_opts.one_blob)
266 n -= (g_blobcols - 2);
267 return n;
268 }
269
270 static const Col&
getcol(uint i)271 getcol(uint i)
272 {
273 if (i < ncol())
274 return g_col[i];
275 assert(false);
276 return g_col[0];
277 }
278
279 static const Col&
getcol(const char * name)280 getcol(const char* name)
281 {
282 uint i;
283 for (i = 0; i < ncol(); i++)
284 if (strcmp(g_col[i].name, name) == 0)
285 break;
286 return getcol(i);
287 }
288
289 struct Tab {
290 char tabname[20];
291 const Col* col;
292 const NdbDictionary::Table* tab;
293 char evtname[20];
294 const NdbDictionary::Event* evt;
TabTab295 Tab(uint idx) :
296 col(g_col),
297 tab(0),
298 evt(0)
299 {
300 sprintf(tabname, "tem%d", idx);
301 sprintf(evtname, "tem%dev", idx);
302 }
303 };
304
305 static Tab* g_tablst[g_maxtab];
306
307 static uint
maxtab()308 maxtab()
309 {
310 return g_opts.maxtab;
311 }
312
313 static Tab&
tab(uint i)314 tab(uint i)
315 {
316 assert(i < maxtab() && g_tablst[i] != 0);
317 return *g_tablst[i];
318 }
319
320 static int
createtable(Tab & t)321 createtable(Tab& t)
322 {
323 ll2("createtable: " << t.tabname);
324 t.tab = 0;
325 NdbDictionary::Table tab(t.tabname);
326 tab.setLogging(false);
327 CHARSET_INFO* cs;
328 chkrc((cs = get_charset_by_name(g_csname, MYF(0))) != 0);
329 uint i;
330 for (i = 0; i < ncol(); i++) {
331 const Col& c = t.col[i];
332 NdbDictionary::Column col(c.name);
333 col.setType(c.type);
334 col.setPrimaryKey(c.pk);
335 col.setNullable(c.nullable);
336 switch (c.type) {
337 case NdbDictionary::Column::Unsigned:
338 break;
339 case NdbDictionary::Column::Char:
340 case NdbDictionary::Column::Varchar:
341 col.setLength(c.length);
342 col.setCharset(cs);
343 break;
344 case NdbDictionary::Column::Text:
345 col.setBlobVersion(g_opts.blob_version);
346 col.setInlineSize(c.inlinesize);
347 col.setPartSize(c.partsize);
348 col.setStripeSize(g_opts.blob_version == 1 ? 4 : c.stripesize);
349 col.setCharset(cs);
350 break;
351 case NdbDictionary::Column::Blob:
352 col.setBlobVersion(g_opts.blob_version);
353 col.setInlineSize(c.inlinesize);
354 col.setPartSize(c.partsize);
355 col.setStripeSize(g_opts.blob_version == 1 ? 4 : c.stripesize);
356 break;
357 default:
358 assert(false);
359 break;
360 }
361 tab.addColumn(col);
362 }
363 g_dic = g_ndb->getDictionary();
364 if (! g_opts.use_table) {
365 if (g_dic->getTable(t.tabname) != 0)
366 chkdb(g_dic->dropTable(t.tabname) == 0);
367 chkdb(g_dic->createTable(tab) == 0);
368 }
369 chkdb((t.tab = g_dic->getTable(t.tabname)) != 0);
370 g_dic = 0;
371 if (! g_opts.use_table) {
372 // extra row for GCI probe
373 chkdb((g_con = g_ndb->startTransaction()) != 0);
374 chkdb((g_op = g_con->getNdbOperation(t.tabname)) != 0);
375 chkdb(g_op->insertTuple() == 0);
376 Uint32 pk1;
377 char pk2[1 + g_charlen + 1];
378 pk1 = g_maxpk;
379 sprintf(pk2 + 1, "%-u", pk1);
380 *(uchar*)pk2 = (uchar)(strlen(pk2 + 1));
381 chkdb(g_op->equal("pk1", (char*)&pk1) == 0);
382 chkdb(g_op->equal("pk2", (char*)&pk2[0]) == 0);
383 chkdb(g_con->execute(Commit) == 0);
384 g_ndb->closeTransaction(g_con);
385 g_op = 0;
386 g_con = 0;
387 }
388 return 0;
389 }
390
391 static int
createtables()392 createtables()
393 {
394 ll1("createtables");
395 for (uint i = 0; i < maxtab(); i++)
396 chkrc(createtable(tab(i)) == 0);
397 return 0;
398 }
399
400 static int
droptable(Tab & t)401 droptable(Tab& t)
402 {
403 ll2("droptable: " << t.tabname);
404 if (! g_opts.use_table) {
405 g_dic = g_ndb->getDictionary();
406 chkdb(g_dic->dropTable(t.tabname) == 0);
407 t.tab = 0;
408 g_dic = 0;
409 }
410 return 0;
411 }
412
413 static int
droptables()414 droptables()
415 {
416 ll1("droptables");
417 for (uint i = 0; i < maxtab(); i++)
418 chkrc(droptable(tab(i)) == 0);
419 return 0;
420 }
421
422 static int
createevent(Tab & t)423 createevent(Tab& t)
424 {
425 ll2("createevent: " << t.evtname);
426 t.evt = 0;
427 g_dic = g_ndb->getDictionary();
428 NdbDictionary::Event evt(t.evtname);
429 assert(t.tab != 0);
430 evt.setTable(*t.tab);
431 evt.addTableEvent(NdbDictionary::Event::TE_ALL);
432 uint i;
433 for (i = 0; i < ncol(); i++) {
434 const Col& c = g_col[i];
435 evt.addEventColumn(c.name);
436 }
437 evt.setReport(NdbDictionary::Event::ER_UPDATED);
438 evt.mergeEvents(! g_opts.separate_events);
439 #if 0 // XXX random bugs
440 if (g_dic->getEvent(t.evtname) != 0)
441 chkdb(g_dic->dropEvent(t.evtname) == 0);
442 #else
443 (void)g_dic->dropEvent(t.evtname);
444 chkdb(g_dic->createEvent(evt) == 0);
445 #endif
446 chkdb((t.evt = g_dic->getEvent(t.evtname)) != 0);
447 g_dic = 0;
448 return 0;
449 }
450
451 static int
createevents()452 createevents()
453 {
454 ll1("createevents");
455 for (uint i = 0; i < maxtab(); i++)
456 chkrc(createevent(tab(i)) == 0);
457 return 0;
458 }
459
460 static int
dropevent(Tab & t,bool force=false)461 dropevent(Tab& t, bool force = false)
462 {
463 ll2("dropevent: " << t.evtname);
464 g_dic = g_ndb->getDictionary();
465 chkdb(g_dic->dropEvent(t.evtname) == 0 || force);
466 t.evt = 0;
467 g_dic = 0;
468 return 0;
469 }
470
471 static int
dropevents(bool force=false)472 dropevents(bool force = false)
473 {
474 ll1("dropevents");
475 for (uint i = 0; i < maxtab(); i++) {
476 if (force && g_tablst[i] == 0)
477 continue;
478 chkrc(dropevent(tab(i), force) == 0 || force);
479 }
480 return 0;
481 }
482
483 struct Data {
484 struct Txt { char* val; uint len; };
485 union Ptr { Uint32* u32; char* ch; uchar* uch; Txt* txt; void* v; };
486 Uint32 pk1;
487 char pk2[g_charlen + 1];
488 Uint32 seq;
489 char cc1[g_charlen + 1];
490 Txt tx1;
491 Txt tx2;
492 Txt bl1;
493 Ptr ptr[g_maxcol];
494 int ind[g_maxcol]; // -1 = no data, 1 = NULL, 0 = not NULL
495 uint noop; // bit: omit in NdbOperation (implicit NULL INS or no UPD)
496 uint ppeq; // bit: post/pre data value equal in GCI data[0]/data[1]
initData497 void init() {
498 uint i;
499 pk1 = 0;
500 memset(pk2, 0, sizeof(pk2));
501 seq = 0;
502 memset(cc1, 0, sizeof(cc1));
503 tx1.val = tx2.val = bl1.val = 0;
504 tx1.len = tx2.len = bl1.len = 0;
505 ptr[0].ch = pk2;
506 ptr[1].u32 = &seq;
507 ptr[2].u32 = &pk1;
508 ptr[3].ch = cc1;
509 ptr[4].txt = &tx1;
510 ptr[5].txt = &tx2;
511 ptr[6].txt = &bl1;
512 for (i = 0; i < g_maxcol; i++)
513 ind[i] = -1;
514 noop = 0;
515 ppeq = 0;
516 }
freememData517 void freemem() {
518 delete [] tx1.val;
519 delete [] tx2.val;
520 delete [] bl1.val;
521 tx1.val = tx2.val = bl1.val = 0;
522 tx1.len = tx2.len = bl1.len = 0;
523 }
524 };
525
526 static int
cmpcol(const Col & c,const Data & d1,const Data & d2)527 cmpcol(const Col& c, const Data& d1, const Data& d2)
528 {
529 uint i = c.no;
530 if (d1.ind[i] != d2.ind[i])
531 return 1;
532 if (d1.ind[i] == 0) {
533 switch (c.type) {
534 case NdbDictionary::Column::Unsigned:
535 if (*d1.ptr[i].u32 != *d2.ptr[i].u32)
536 return 1;
537 break;
538 case NdbDictionary::Column::Char:
539 if (memcmp(d1.ptr[i].ch, d2.ptr[i].ch, c.size) != 0)
540 return 1;
541 break;
542 case NdbDictionary::Column::Varchar:
543 {
544 uint l1 = d1.ptr[i].uch[0];
545 uint l2 = d2.ptr[i].uch[0];
546 if (l1 != l2)
547 return 1;
548 if (memcmp(d1.ptr[i].ch, d2.ptr[i].ch, l1) != 0)
549 return 1;
550 }
551 break;
552 case NdbDictionary::Column::Text:
553 case NdbDictionary::Column::Blob:
554 {
555 const Data::Txt& t1 = *d1.ptr[i].txt;
556 const Data::Txt& t2 = *d2.ptr[i].txt;
557 if (t1.len != t2.len)
558 return 1;
559 if (memcmp(t1.val, t2.val, t1.len) != 0)
560 return 1;
561 }
562 break;
563 default:
564 assert(false);
565 break;
566 }
567 }
568 return 0;
569 }
570
571 static NdbOut&
operator <<(NdbOut & out,const Data & d)572 operator<<(NdbOut& out, const Data& d)
573 {
574 uint i;
575 for (i = 0; i < ncol(); i++) {
576 const Col& c = getcol(i);
577 out << (i == 0 ? "" : " ") << c.name;
578 out << (! (d.noop & (1 << i)) ? "=" : ":");
579 if (d.ind[i] == -1)
580 continue;
581 if (d.ind[i] == 1) {
582 out << "NULL";
583 continue;
584 }
585 switch (c.type) {
586 case NdbDictionary::Column::Unsigned:
587 out << *d.ptr[i].u32;
588 break;
589 case NdbDictionary::Column::Char:
590 {
591 char buf[g_charlen + 1];
592 memcpy(buf, d.ptr[i].ch, g_charlen);
593 uint n = g_charlen;
594 while (1) {
595 buf[n] = 0;
596 if (n == 0 || buf[n - 1] != 0x20)
597 break;
598 n--;
599 }
600 out << "'" << buf << "'";
601 }
602 break;
603 case NdbDictionary::Column::Varchar:
604 {
605 char buf[g_charlen + 1];
606 uint l = d.ptr[i].uch[0];
607 assert(l <= g_charlen);
608 memcpy(buf, &d.ptr[i].ch[1], l);
609 buf[l] = 0;
610 out << "'" << buf << "'";
611 }
612 break;
613 case NdbDictionary::Column::Text:
614 case NdbDictionary::Column::Blob:
615 {
616 Data::Txt& txt = *d.ptr[i].txt;
617 bool first = true;
618 uint j = 0;
619 while (j < txt.len) {
620 char c[2];
621 c[0] = txt.val[j++];
622 c[1] = 0;
623 uint m = 1;
624 while (j < txt.len && txt.val[j] == c[0])
625 j++, m++;
626 if (! first)
627 out << "+";
628 first = false;
629 out << m << c;
630 }
631 }
632 break;
633 default:
634 assert(false);
635 break;
636 }
637 }
638 return out;
639 }
640
641 // some random os may define these
642 #undef UNDEF
643 #undef INS
644 #undef DEL
645 #undef UPD
646 #undef NUL
647
648 static const uint g_optypes = 3; // real ops 0-2
649
650 /*
651 * Represents single or composite operation or received event. The
652 * post/pre data is either computed here for operations or received from
653 * the event.
654 */
655 struct Op { // single or composite
656 enum Kind { OP = 1, EV = 2 };
657 enum Type { UNDEF = -1, INS, DEL, UPD, NUL };
658 Kind kind;
659 Type type;
660 Op* next_op; // within one commit
661 Op* next_com; // next commit chain
662 Op* next_gci; // groups commit chains (unless --separate-events)
663 Op* next_ev; // next event
664 Op* next_free; // free list
665 bool free; // on free list
666 uint num_op;
667 uint num_com;
668 Data data[2]; // 0-post 1-pre
669 bool match; // matched to event
670 Uint64 gci; // defined for com op and event
initOp671 void init(Kind a_kind, Type a_type = UNDEF) {
672 kind = a_kind;
673 assert(kind == OP || kind == EV);
674 type = a_type;
675 next_op = next_com = next_gci = next_ev = next_free = 0;
676 free = false;
677 num_op = num_com = 0;
678 data[0].init();
679 data[1].init();
680 match = false;
681 gci = 0;
682 }
freememOp683 void freemem() {
684 data[0].freemem();
685 data[1].freemem();
686 }
687 };
688
689 static NdbOut&
operator <<(NdbOut & out,Op::Type optype)690 operator<<(NdbOut& out, Op::Type optype)
691 {
692 switch (optype) {
693 case Op::INS:
694 out << "INS";
695 break;
696 case Op::DEL:
697 out << "DEL";
698 break;
699 case Op::UPD:
700 out << "UPD";
701 break;
702 case Op::NUL:
703 out << "NUL";
704 break;
705 default:
706 out << (int)optype;
707 break;
708 }
709 return out;
710 }
711
712 static NdbOut&
operator <<(NdbOut & out,const Op & op)713 operator<<(NdbOut& out, const Op& op)
714 {
715 out << op.type;
716 out << " " << op.data[0];
717 out << " [" << op.data[1] << "]";
718 if (op.gci != 0)
719 out << " gci:" << op.gci;
720 return out;
721 }
722
723 static int
seteventtype(Op * ev,NdbDictionary::Event::TableEvent te)724 seteventtype(Op* ev, NdbDictionary::Event::TableEvent te)
725 {
726 Op::Type optype = Op::UNDEF;
727 switch (te) {
728 case NdbDictionary::Event::TE_INSERT:
729 optype = Op::INS;
730 break;
731 case NdbDictionary::Event::TE_DELETE:
732 optype = Op::DEL;
733 break;
734 case NdbDictionary::Event::TE_UPDATE:
735 optype = Op::UPD;
736 break;
737 default:
738 ll0("EVT: " << *ev << ": bad event type " << hex << (uint)te);
739 return -1;
740 }
741 ev->type = optype;
742 return 0;
743 }
744
745 struct Counter { // debug aid
746 const char* name;
747 uint count;
CounterCounter748 Counter(const char* a_name) : name(a_name), count(0) {
749 }
operator <<(NdbOut & out,const Counter & counter)750 friend class NdbOut& operator<<(NdbOut& out, const Counter& counter) {
751 out << counter.name << "(" << counter.count << ")";
752 return out;
753 }
operator uintCounter754 operator uint() {
755 return count;
756 }
operator ++Counter757 Counter operator ++(int) {
758 ll3(*this << "++");
759 Counter tmp = *this;
760 count++;
761 return tmp;
762 }
operator --Counter763 Counter operator --(int) {
764 ll3(*this << "--");
765 assert(count != 0);
766 Counter tmp = *this;
767 count--;
768 return tmp;
769 }
770 };
771
772 static Op* g_opfree = 0;
773 static uint g_freeops = 0;
774 static uint g_usedops = 0;
775 static uint g_gciops = 0;
776 static uint g_maxcom = 10; // max ops per commit
777 static uint g_seq = 0;
778 static Op* g_rec_ev;
779 static uint g_num_ev = 0;
780
781 static const uint g_maxgcis = 500; // max GCIs seen during 1 loop
782
783 // operation data per table and each loop
784 struct Run : public Tab {
785 bool skip; // no ops in current loop
786 NdbEventOperation* evt_op;
787 uint gcicnt; // number of CGIs seen in current loop
788 Uint64 gcinum[g_maxgcis];
789 Uint32 gcievtypes[g_maxgcis][2]; // 0-getGCIEventOperations 1-nextEvent
790 uint tableops; // real table ops in this loop
791 uint blobops; // approx blob part ops in this loop
792 uint gciops; // commit chains or (after mergeops) gci chains
793 Op* pk_op[g_maxpk]; // GCI chain of ops per PK
794 Op* pk_ev[g_maxpk]; // events per PK
795 uint ev_pos[g_maxpk]; // counts events
796 NdbRecAttr* ev_ra[2][g_maxcol]; // 0-post 1-pre
797 NdbBlob* ev_bh[2][g_maxcol]; // 0-post 1-pre
RunRun798 Run(uint idx) :
799 Tab(idx)
800 {
801 reset();
802 }
resetRun803 void reset()
804 {
805 int i, j;
806 skip = false;
807 evt_op = 0;
808 gcicnt = 0;
809 for (i = 0; i < (int)g_maxgcis; i++) {
810 gcinum[i] = (Uint64)0;
811 gcievtypes[i][0] = gcievtypes[i][1] = (Uint32)0;
812 }
813 tableops = 0;
814 blobops = 0;
815 gciops = 0;
816 for (i = 0; i < (int)g_maxpk; i++) {
817 pk_op[i] = 0;
818 pk_ev[i] = 0;
819 ev_pos[i] = 0;
820 }
821 for (j = 0; i < 2; j ++) {
822 for (i = 0; i < (int)g_maxcol; i++) {
823 ev_ra[j][i] = 0;
824 ev_bh[j][i] = 0;
825 }
826 }
827 }
addgciRun828 int addgci(Uint64 gci)
829 {
830 assert(gcicnt < g_maxgcis);
831 chkrc(gcicnt == 0 || gcinum[gcicnt - 1] < gci);
832 gcinum[gcicnt++] = gci;
833 return 0;
834 }
addevtypesRun835 void addevtypes(Uint64 gci, Uint32 evtypes, uint i)
836 {
837 assert(gcicnt != 0 && gci == gcinum[gcicnt - 1]);
838 assert(evtypes != 0);
839 assert(i < 2);
840 gcievtypes[gcicnt - 1][i] |= evtypes;
841 }
842 };
843
844 static Run* g_runlst[g_maxtab];
845
846 static uint
maxrun()847 maxrun()
848 {
849 return maxtab();
850 }
851
852 static Run&
run(uint i)853 run(uint i)
854 {
855 assert(i < maxrun() && g_runlst[i] != 0);
856 return *g_runlst[i];
857 }
858
859 static void
initrun()860 initrun()
861 {
862 uint i;
863 for (i = 0; i < maxrun(); i++)
864 g_tablst[i] = g_runlst[i] = new Run(i);
865 }
866
867 static Op*
getop(Op::Kind a_kind,Op::Type a_type=Op::UNDEF)868 getop(Op::Kind a_kind, Op::Type a_type = Op::UNDEF)
869 {
870 if (g_opfree == 0) {
871 assert(g_freeops == 0);
872 Op* op = new Op;
873 assert(op != 0);
874 op->next_free = g_opfree; // 0
875 g_opfree = op;
876 op->free = true;
877 g_freeops++;
878 }
879 Op* op = g_opfree;
880 g_opfree = op->next_free;
881 assert(g_freeops != 0);
882 g_freeops--;
883 g_usedops++;
884 op->init(a_kind, a_type);
885 op->free = false;
886 ll3("getop: " << op);
887 return op;
888 }
889
890 static void
freeop(Op * op)891 freeop(Op* op)
892 {
893 ll3("freeop: " << op);
894 assert(! op->free);
895 op->freemem();
896 op->free = true;
897 op->next_free = g_opfree;
898 g_opfree = op;
899 g_freeops++;
900 assert(g_usedops != 0);
901 g_usedops--;
902 }
903
904 static void
resetmem(Run & r)905 resetmem(Run& r)
906 {
907 ll2("resetmem");
908 Uint32 pk1;
909 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++)
910 r.ev_pos[pk1] = 0;
911 // leave g_seq
912 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
913 if (r.pk_op[pk1] != 0) {
914 Op* tot_op = r.pk_op[pk1];
915 while (tot_op->next_gci != 0) {
916 Op* gci_op = tot_op->next_gci;
917 while (gci_op->next_com != 0) {
918 Op* com_op = gci_op->next_com;
919 while (com_op->next_op != 0) {
920 Op* op = com_op->next_op;
921 com_op->next_op = op->next_op;
922 freeop(op);
923 }
924 gci_op->next_com = com_op->next_com;
925 freeop(com_op);
926 }
927 tot_op->next_gci = gci_op->next_gci;
928 freeop(gci_op);
929 }
930 freeop(tot_op);
931 r.pk_op[pk1] = 0;
932 }
933 if (r.pk_ev[pk1] != 0) {
934 Op* tot_op = r.pk_ev[pk1];
935 while (tot_op->next_ev != 0) {
936 Op* ev = tot_op->next_ev;
937 tot_op->next_ev = ev->next_ev;
938 freeop(ev);
939 }
940 freeop(tot_op);
941 r.pk_ev[pk1] = 0;
942 }
943 }
944 r.reset();
945 }
946
947 static void
resetmem()948 resetmem()
949 {
950 if (g_rec_ev != 0) {
951 freeop(g_rec_ev);
952 g_rec_ev = 0;
953 }
954 for (uint i = 0; i < maxrun(); i++)
955 resetmem(run(i));
956 assert(g_usedops == 0);
957 g_gciops = g_num_ev = 0;
958 }
959
960 static void
deleteops()961 deleteops() // for memleak checks
962 {
963 while (g_opfree != 0) {
964 Op* tmp_op = g_opfree;
965 g_opfree = g_opfree->next_free;
966 delete tmp_op;
967 g_freeops--;
968 }
969 assert(g_freeops == 0);
970 }
971
972 struct Comp {
973 Op::Type t1, t2, t3;
974 };
975
976 static Comp
977 g_comp[] = {
978 { Op::INS, Op::DEL, Op::NUL },
979 { Op::INS, Op::UPD, Op::INS },
980 { Op::DEL, Op::INS, Op::UPD },
981 { Op::UPD, Op::DEL, Op::DEL },
982 { Op::UPD, Op::UPD, Op::UPD }
983 };
984
985 static const uint g_ncomp = sizeof(g_comp)/sizeof(g_comp[0]);
986
987 static int
checkop(const Op * op,Uint32 & pk1)988 checkop(const Op* op, Uint32& pk1)
989 {
990 Op::Type optype = op->type;
991 assert(optype != Op::UNDEF);
992 if (optype == Op::NUL)
993 return 0;
994 chkrc(optype == Op::INS || optype == Op::DEL || optype == Op::UPD);
995 const Data& d0 = op->data[0];
996 const Data& d1 = op->data[1];
997 {
998 const Col& c = getcol("pk1");
999 chkrc(d0.ind[c.no] == 0);
1000 pk1 = d0.pk1;
1001 chkrc(pk1 < g_opts.maxpk);
1002 }
1003 uint i;
1004 for (i = 0; i < ncol(); i++) {
1005 const Col& c = getcol(i);
1006 const int ind0 = d0.ind[i];
1007 const int ind1 = d1.ind[i];
1008 // the rules are the rules..
1009 if (c.pk) {
1010 chkrc(ind0 == 0); // always PK in post data
1011 if (optype == Op::INS)
1012 chkrc(ind1 == -1); // no PK in pre data
1013 if (optype == Op::DEL)
1014 chkrc(ind1 == 0); // always PK in pre data (note change from 6.3.23)
1015 if (optype == Op::UPD)
1016 chkrc(ind1 == 0); // always PK in pre data
1017 }
1018 if (! c.pk) {
1019 if (optype == Op::INS)
1020 chkrc(ind0 >= 0 && ind1 == -1);
1021 if (optype == Op::DEL)
1022 chkrc(ind0 == -1 && ind1 >= 0); // always non-PK in pre data
1023 if (optype == Op::UPD)
1024 chkrc(ind0 == -1 || ind1 >= 0); // update must have pre data
1025 }
1026 if (! c.nullable) {
1027 chkrc(ind0 <= 0 && ind1 <= 0);
1028 }
1029 if (c.isblob()) {
1030 // blob values must be from allowed chars
1031 int j;
1032 for (j = 0; j < 2; j++) {
1033 const Data& d = op->data[j];
1034 if (d.ind[i] == 0) {
1035 const Data::Txt& txt = *d.ptr[i].txt;
1036 int k;
1037 for (k = 0; k < (int)txt.len; k++) {
1038 chkrc(strchr(g_charval, txt.val[k]) != 0);
1039 }
1040 }
1041 }
1042 }
1043 }
1044 return 0;
1045 }
1046
1047 static Comp*
comptype(Op::Type t1,Op::Type t2)1048 comptype(Op::Type t1, Op::Type t2) // only non-NUL
1049 {
1050 uint i;
1051 for (i = 0; i < g_ncomp; i++)
1052 if (g_comp[i].t1 == t1 && g_comp[i].t2 == t2)
1053 return &g_comp[i];
1054 return 0;
1055 }
1056
1057 static void
copycol(const Col & c,const Data & d1,Data & d3)1058 copycol(const Col& c, const Data& d1, Data& d3)
1059 {
1060 uint i = c.no;
1061 if ((d3.ind[i] = d1.ind[i]) == 0) {
1062 if (! c.isblob()) {
1063 memmove(d3.ptr[i].v, d1.ptr[i].v, c.size);
1064 } else {
1065 Data::Txt& t1 = *d1.ptr[i].txt;
1066 Data::Txt& t3 = *d3.ptr[i].txt;
1067 delete [] t3.val;
1068 t3.val = new char [t1.len];
1069 t3.len = t1.len;
1070 memcpy(t3.val, t1.val, t1.len);
1071 }
1072 }
1073 }
1074
1075 static void
copydata(const Data & d1,Data & d3,bool pk,bool nonpk)1076 copydata(const Data& d1, Data& d3, bool pk, bool nonpk)
1077 {
1078 uint i;
1079 for (i = 0; i < ncol(); i++) {
1080 const Col& c = g_col[i];
1081 if ((c.pk && pk) || (! c.pk && nonpk))
1082 copycol(c, d1, d3);
1083 }
1084 }
1085
1086 static void
compdata(const Data & d1,const Data & d2,Data & d3,bool pk,bool nonpk)1087 compdata(const Data& d1, const Data& d2, Data& d3, bool pk, bool nonpk)
1088 {
1089 uint i;
1090 for (i = 0; i < ncol(); i++) {
1091 const Col& c = g_col[i];
1092 if ((c.pk && pk) || (! c.pk && nonpk)) {
1093 const Data* d = 0;
1094 if (d1.ind[i] == -1 && d2.ind[i] == -1)
1095 d3.ind[i] = -1;
1096 else if (d1.ind[i] == -1 && d2.ind[i] != -1)
1097 d = &d2;
1098 else if (d1.ind[i] != -1 && d2.ind[i] == -1)
1099 d = &d1;
1100 else
1101 d = &d2;
1102 if (d != 0)
1103 copycol(c, *d, d3);
1104 }
1105 }
1106 }
1107
1108 static void
copyop(const Op * op1,Op * op3)1109 copyop(const Op* op1, Op* op3)
1110 {
1111 op3->type = op1->type;
1112 copydata(op1->data[0], op3->data[0], true, true);
1113 copydata(op1->data[1], op3->data[1], true, true);
1114 op3->gci = op1->gci;
1115 Uint32 pk1_tmp;
1116 reqrc(checkop(op3, pk1_tmp) == 0);
1117 }
1118
1119 static int
compop(const Op * op1,const Op * op2,Op * op3)1120 compop(const Op* op1, const Op* op2, Op* op3) // op1 o op2 = op3
1121 {
1122 assert(op1->type != Op::UNDEF && op2->type != Op::UNDEF);
1123 Comp* comp;
1124 if (op2->type == Op::NUL) {
1125 copyop(op1, op3);
1126 return 0;
1127 }
1128 if (op1->type == Op::NUL) {
1129 copyop(op2, op3);
1130 return 0;
1131 }
1132 Op::Kind kind =
1133 op1->kind == Op::OP && op2->kind == Op::OP ? Op::OP : Op::EV;
1134 Op* res_op = getop(kind);
1135 chkrc((comp = comptype(op1->type, op2->type)) != 0);
1136 res_op->type = comp->t3;
1137 if (res_op->type == Op::INS) {
1138 // INS o UPD
1139 compdata(op1->data[0], op2->data[0], res_op->data[0], true, true);
1140 // pre = undef
1141 }
1142 if (res_op->type == Op::DEL) {
1143 // UPD o DEL
1144 copydata(op2->data[0], res_op->data[0], true, false); // PK only
1145 copydata(op1->data[1], res_op->data[1], true, true); // PK + non-PK
1146 }
1147 if (res_op->type == Op::UPD && op1->type == Op::DEL) {
1148 // DEL o INS
1149 copydata(op2->data[0], res_op->data[0], true, true);
1150 copydata(op1->data[0], res_op->data[1], true, false); // PK only
1151 copydata(op1->data[1], res_op->data[1], true, true); // PK + non-PK
1152 }
1153 if (res_op->type == Op::UPD && op1->type == Op::UPD) {
1154 // UPD o UPD
1155 compdata(op1->data[0], op2->data[0], res_op->data[0], true, true);
1156 compdata(op2->data[1], op1->data[1], res_op->data[1], true, true);
1157 }
1158 assert(op1->gci == op2->gci);
1159 res_op->gci = op2->gci;
1160 Uint32 pk1_tmp;
1161 reqrc(checkop(res_op, pk1_tmp) == 0);
1162 copyop(res_op, op3);
1163 freeop(res_op);
1164 return 0;
1165 }
1166
1167 static int
createeventop(Run & r)1168 createeventop(Run& r)
1169 {
1170 ll2("createeventop: " << r.tabname);
1171 chkdb((r.evt_op = g_ndb->createEventOperation(r.evtname)) != 0);
1172 r.evt_op->mergeEvents(! g_opts.separate_events); // not yet inherited
1173 uint i;
1174 for (i = 0; i < ncol(); i++) {
1175 const Col& c = g_col[i];
1176 Data (&d)[2] = g_rec_ev->data;
1177 if (! c.isblob()) {
1178 chkdb((r.ev_ra[0][i] = r.evt_op->getValue(c.name, (char*)d[0].ptr[i].v)) != 0);
1179 chkdb((r.ev_ra[1][i] = r.evt_op->getPreValue(c.name, (char*)d[1].ptr[i].v)) != 0);
1180 } else {
1181 chkdb((r.ev_bh[0][i] = r.evt_op->getBlobHandle(c.name)) != 0);
1182 chkdb((r.ev_bh[1][i] = r.evt_op->getPreBlobHandle(c.name)) != 0);
1183 }
1184 }
1185 return 0;
1186 }
1187
1188 static int
createeventop()1189 createeventop()
1190 {
1191 ll1("createeventop");
1192 for (uint i = 0; i < maxrun(); i++)
1193 chkrc(createeventop(run(i)) == 0);
1194 return 0;
1195 }
1196
1197 static int
executeeventop(Run & r)1198 executeeventop(Run& r)
1199 {
1200 ll2("executeeventop: " << r.tabname);
1201 chkdb(r.evt_op->execute() == 0);
1202 return 0;
1203 }
1204
1205 static int
executeeventop()1206 executeeventop()
1207 {
1208 ll1("executeeventop");
1209 for (uint i = 0; i < maxrun(); i++)
1210 chkrc(executeeventop(run(i)) == 0);
1211 return 0;
1212 }
1213
1214 static int
dropeventop(Run & r,bool force=false)1215 dropeventop(Run& r, bool force = false)
1216 {
1217 ll2("dropeventop: " << r.tabname);
1218 if (r.evt_op != 0) {
1219 chkdb(g_ndb->dropEventOperation(r.evt_op) == 0 || force);
1220 r.evt_op = 0;
1221 }
1222 return 0;
1223 }
1224
1225 static int
dropeventops(bool force=false)1226 dropeventops(bool force = false)
1227 {
1228 ll1("dropeventops");
1229 for (uint i = 0; i < maxrun(); i++) {
1230 if (force && g_runlst[i] == 0)
1231 continue;
1232 chkrc(dropeventop(run(i), force) == 0 || force);
1233 }
1234 return 0;
1235 }
1236
1237 // wait for event to be installed and for GCIs to pass
1238 static int
waitgci(uint ngci)1239 waitgci(uint ngci)
1240 {
1241 ll1("waitgci " << ngci);
1242 Uint64 gci[2];
1243 uint i = 0;
1244 while (1) {
1245 chkdb((g_con = g_ndb->startTransaction()) != 0);
1246 { // forced to exec a dummy op
1247 Tab& t = tab(0); // use first table
1248 Uint32 pk1;
1249 char pk2[1 + g_charlen + 1];
1250 pk1 = g_maxpk;
1251 sprintf(pk2 + 1, "%-u", pk1);
1252 *(uchar*)pk2 = (uchar)(strlen(pk2 + 1));
1253 chkdb((g_op = g_con->getNdbOperation(t.tabname)) != 0);
1254 chkdb(g_op->readTuple() == 0);
1255 chkdb(g_op->equal("pk1", (char*)&pk1) == 0);
1256 chkdb(g_op->equal("pk2", (char*)&pk2[0]) == 0);
1257 chkdb(g_con->execute(Commit) == 0);
1258 g_op = 0;
1259 }
1260 g_con->getGCI(&gci[i]);
1261 g_ndb->closeTransaction(g_con);
1262 g_con = 0;
1263 if (i == 1 && gci[0] + ngci <= gci[1]) {
1264 ll1("waitgci: " << gci[0] << " " << gci[1]);
1265 break;
1266 }
1267 i = 1;
1268 sleep(1);
1269 }
1270 return 0;
1271 }
1272
1273 // scan table and set current tot_op for each pk1
1274 static int
scantable(Run & r)1275 scantable(Run& r)
1276 {
1277 ll2("scantable: " << r.tabname);
1278 NdbRecAttr* ra[g_maxcol];
1279 NdbBlob* bh[g_maxcol];
1280 Op* rec_op = getop(Op::OP);
1281 Data& d0 = rec_op->data[0];
1282 chkdb((g_con = g_ndb->startTransaction()) != 0);
1283 chkdb((g_scan_op = g_con->getNdbScanOperation(r.tabname)) != 0);
1284 chkdb(g_scan_op->readTuples() == 0);
1285 uint i;
1286 for (i = 0; i < ncol(); i++) {
1287 const Col& c = getcol(i);
1288 if (! c.isblob()) {
1289 chkdb((ra[i] = g_scan_op->getValue(c.name, (char*)d0.ptr[i].v)) != 0);
1290 } else {
1291 chkdb((bh[i] = g_scan_op->getBlobHandle(c.name)) != 0);
1292 }
1293 }
1294 chkdb(g_con->execute(NoCommit) == 0);
1295 int ret;
1296 while ((ret = g_scan_op->nextResult()) == 0) {
1297 Uint32 pk1 = d0.pk1;
1298 if (pk1 >= g_opts.maxpk)
1299 continue;
1300 rec_op->type = Op::INS;
1301 for (i = 0; i < ncol(); i++) {
1302 const Col& c = getcol(i);
1303 int ind;
1304 if (! c.isblob()) {
1305 ind = ra[i]->isNULL();
1306 } else {
1307 int ret;
1308 ret = bh[i]->getDefined(ind);
1309 assert(ret == 0);
1310 if (ind == 0) {
1311 Data::Txt& txt = *d0.ptr[i].txt;
1312 Uint64 len64;
1313 ret = bh[i]->getLength(len64);
1314 assert(ret == 0);
1315 txt.len = (uint)len64;
1316 delete [] txt.val;
1317 txt.val = new char [txt.len];
1318 memset(txt.val, 'X', txt.len);
1319 Uint32 len = txt.len;
1320 ret = bh[i]->readData(txt.val, len);
1321 assert(ret == 0 && len == txt.len);
1322 // to see the data, have to execute...
1323 chkdb(g_con->execute(NoCommit) == 0);
1324 assert(memchr(txt.val, 'X', txt.len) == 0);
1325 }
1326 }
1327 assert(ind >= 0);
1328 d0.ind[i] = ind;
1329 }
1330 assert(r.pk_op[pk1] == 0);
1331 Op* tot_op = r.pk_op[pk1] = getop(Op::OP);
1332 copyop(rec_op, tot_op);
1333 tot_op->type = Op::INS;
1334 }
1335 chkdb(ret == 1);
1336 g_ndb->closeTransaction(g_con);
1337 g_scan_op = 0;
1338 g_con = 0;
1339 freeop(rec_op);
1340 return 0;
1341 }
1342
1343 static int
scantable()1344 scantable()
1345 {
1346 ll1("scantable");
1347 for (uint i = 0; i < maxrun(); i++)
1348 chkrc(scantable(run(i)) == 0);
1349 return 0;
1350 }
1351
1352 static void
makedata(const Col & c,Data & d,Uint32 pk1,Op::Type optype)1353 makedata(const Col& c, Data& d, Uint32 pk1, Op::Type optype)
1354 {
1355 uint i = c.no;
1356 if (c.pk) {
1357 switch (c.type) {
1358 case NdbDictionary::Column::Unsigned:
1359 {
1360 Uint32* p = d.ptr[i].u32;
1361 *p = pk1;
1362 }
1363 break;
1364 case NdbDictionary::Column::Char:
1365 {
1366 char* p = d.ptr[i].ch;
1367 sprintf(p, "%-*u", g_charlen, pk1);
1368 }
1369 break;
1370 case NdbDictionary::Column::Varchar:
1371 {
1372 char* p = &d.ptr[i].ch[1];
1373 sprintf(p, "%-u", pk1);
1374 uint len = pk1 % g_charlen;
1375 uint j = strlen(p);
1376 while (j < len) {
1377 p[j] = 'a' + j % 26;
1378 j++;
1379 }
1380 d.ptr[i].uch[0] = len;
1381 }
1382 break;
1383 default:
1384 assert(false);
1385 break;
1386 }
1387 d.ind[i] = 0;
1388 } else if (optype == Op::DEL) {
1389 ;
1390 } else if (i == getcol("seq").no) {
1391 d.seq = g_seq++;
1392 d.ind[i] = 0;
1393 } else if (optype == Op::INS && ! g_opts.no_implicit_nulls && c.nullable && urandom(10, 100)) {
1394 d.noop |= (1 << i);
1395 d.ind[i] = 1; // implicit NULL value is known
1396 } else if (optype == Op::UPD && ! g_opts.no_missing_update && urandom(10, 100)) {
1397 d.noop |= (1 << i);
1398 d.ind[i] = -1; // fixed up in caller
1399 } else if (! g_opts.no_nulls && c.nullable && urandom(10, 100)) {
1400 d.ind[i] = 1;
1401 } else {
1402 switch (c.type) {
1403 case NdbDictionary::Column::Unsigned:
1404 {
1405 Uint32* p = d.ptr[i].u32;
1406 uint u = urandom();
1407 *p = u;
1408 }
1409 break;
1410 case NdbDictionary::Column::Char:
1411 {
1412 char* p = d.ptr[i].ch;
1413 uint u = urandom(g_charlen);
1414 if (u == 0)
1415 u = urandom(g_charlen); // 2x bias for non-empty
1416 uint j;
1417 for (j = 0; j < g_charlen; j++) {
1418 uint v = urandom(strlen(g_charval));
1419 p[j] = j < u ? g_charval[v] : 0x20;
1420 }
1421 }
1422 break;
1423 case NdbDictionary::Column::Text:
1424 case NdbDictionary::Column::Blob:
1425 {
1426 const bool tinyblob = (c.type == NdbDictionary::Column::Blob);
1427 Data::Txt& txt = *d.ptr[i].txt;
1428 delete [] txt.val;
1429 txt.val = 0;
1430 if (g_opts.tweak & 1) {
1431 uint u = g_blobinlinesize + (tinyblob ? 0 : g_blobpartsize);
1432 uint v = (g_opts.tweak & 2) ? 0 : urandom(strlen(g_charval));
1433 txt.val = new char [u];
1434 txt.len = u;
1435 memset(txt.val, g_charval[v], u);
1436 break;
1437 }
1438 uint u = urandom(tinyblob ? g_blobinlinesize : g_maxblobsize);
1439 u = urandom(u); // 4x bias for smaller blobs
1440 u = urandom(u);
1441 txt.val = new char [u];
1442 txt.len = u;
1443 uint j = 0;
1444 while (j < u) {
1445 assert(u > 0);
1446 uint k = 1 + urandom(u - 1);
1447 if (k > u - j)
1448 k = u - j;
1449 uint v = urandom(strlen(g_charval));
1450 memset(&txt.val[j], g_charval[v], k);
1451 j += k;
1452 }
1453 }
1454 break;
1455 default:
1456 assert(false);
1457 break;
1458 }
1459 d.ind[i] = 0;
1460 }
1461 }
1462
1463 static void
makeop(const Op * prev_op,Op * op,Uint32 pk1,Op::Type optype)1464 makeop(const Op* prev_op, Op* op, Uint32 pk1, Op::Type optype)
1465 {
1466 op->type = optype;
1467 const Data& dp = prev_op->data[0];
1468 Data& d0 = op->data[0];
1469 Data& d1 = op->data[1];
1470 uint i;
1471 for (i = 0; i < ncol(); i++) {
1472 const Col& c = getcol(i);
1473 makedata(c, d0, pk1, optype);
1474 if (optype == Op::INS) {
1475 d1.ind[i] = -1;
1476 } else if (optype == Op::DEL) {
1477 assert(dp.ind[i] >= 0);
1478 copycol(c, dp, d1);
1479 } else if (optype == Op::UPD) {
1480 assert(dp.ind[i] >= 0);
1481 if (d0.ind[i] == -1) // not updating this col
1482 copycol(c, dp, d0); // must keep track of data
1483 copycol(c, dp, d1);
1484 } else {
1485 assert(false);
1486 }
1487 }
1488 Uint32 pk1_tmp = ~(Uint32)0;
1489 reqrc(checkop(op, pk1_tmp) == 0);
1490 reqrc(pk1 == pk1_tmp);
1491 }
1492
1493 static uint
approxblobops(Op * op)1494 approxblobops(Op* op)
1495 {
1496 uint avg_blob_size = g_maxblobsize / 4; // see makedata()
1497 uint avg_blob_ops = avg_blob_size / 2000;
1498 uint n = 0;
1499 if (! g_opts.no_blobs) {
1500 n += avg_blob_ops;
1501 if (! g_opts.one_blob)
1502 n += avg_blob_ops;
1503 if (op->type == Op::UPD)
1504 n *= 2;
1505 }
1506 return n;
1507 }
1508
1509 static void
makeops(Run & r)1510 makeops(Run& r)
1511 {
1512 ll1("makeops: " << r.tabname);
1513 Uint32 pk1 = 0;
1514 while (1) {
1515 if (g_opts.opstring == 0) {
1516 if (r.tableops + r.blobops >= g_opts.maxops) // use up ops
1517 break;
1518 pk1 = urandom(g_opts.maxpk);
1519 } else {
1520 if (pk1 >= g_opts.maxpk) // use up pks
1521 break;
1522 }
1523 ll2("makeops: pk1=" << pk1);
1524 // total op on the pk so far
1525 // optype either NUL=initial/deleted or INS=created
1526 Op* tot_op = r.pk_op[pk1];
1527 if (tot_op == 0)
1528 tot_op = r.pk_op[pk1] = getop(Op::OP, Op::NUL);
1529 assert(tot_op->type == Op::NUL || tot_op->type == Op::INS);
1530 // add new commit chain to end
1531 Op* last_gci = tot_op;
1532 while (last_gci->next_gci != 0)
1533 last_gci = last_gci->next_gci;
1534 Op* gci_op = getop(Op::OP, Op::NUL);
1535 last_gci->next_gci = gci_op;
1536 Op* com_op = getop(Op::OP, Op::NUL);
1537 gci_op->next_com = com_op;
1538 // length of random chain
1539 uint len = ~0;
1540 if (g_opts.opstring == 0) {
1541 len = 1 + urandom(g_maxcom - 1);
1542 len = 1 + urandom(len - 1); // 2x bias for short chain
1543 }
1544 uint n = 0;
1545 while (1) {
1546 // random or from current g_opts.opstring part
1547 Op::Type optype;
1548 if (g_opts.opstring == 0) {
1549 if (n == len)
1550 break;
1551 do {
1552 optype = (Op::Type)urandom(g_optypes);
1553 } while ((tot_op->type == Op::NUL &&
1554 (optype == Op::DEL || optype == Op::UPD)) ||
1555 (tot_op->type == Op::INS && optype == Op::INS));
1556 } else {
1557 const char* str = g_opstringpart[g_loop % g_opstringparts];
1558 uint m = strlen(str);
1559 uint k = tot_op->num_com + tot_op->num_op;
1560 assert(k < m);
1561 char c = str[k];
1562 if (c == 'c') {
1563 if (k + 1 == m)
1564 pk1 += 1;
1565 break;
1566 }
1567 const char* p = "idu";
1568 const char* q = strchr(p, c);
1569 assert(q != 0);
1570 optype = (Op::Type)(q - p);
1571 }
1572 Op* op = getop(Op::OP);
1573 makeop(tot_op, op, pk1, optype);
1574 r.tableops++;
1575 r.blobops += approxblobops(op);
1576 // add to end
1577 Op* last_op = com_op;
1578 while (last_op->next_op != 0)
1579 last_op = last_op->next_op;
1580 last_op->next_op = op;
1581 // merge into chain head and total op
1582 reqrc(compop(com_op, op, com_op) == 0);
1583 reqrc(compop(tot_op, op, tot_op) == 0);
1584 assert(tot_op->type == Op::NUL || tot_op->type == Op::INS);
1585 // counts
1586 com_op->num_op += 1;
1587 tot_op->num_op += 1;
1588 n++;
1589 }
1590 // copy to gci level
1591 copyop(com_op, gci_op);
1592 tot_op->num_com += 1;
1593 r.gciops += 1;
1594 g_gciops += 1;
1595 }
1596 ll1("makeops: " << r.tabname << ": com recs = " << r.gciops);
1597 }
1598
1599 static void
selecttables()1600 selecttables()
1601 {
1602 uint i;
1603 for (i = 0; i < maxrun(); i++)
1604 run(i).skip = false;
1605 if (g_opts.opstring != 0) {
1606 ll1("using all tables due to fixed ops");
1607 return;
1608 }
1609 for (i = 0; i + 1 < maxrun(); i++)
1610 run(urandom(maxrun())).skip = true;
1611 uint cnt = 0;
1612 for (i = 0; i < maxrun(); i++) {
1613 if (! run(i).skip) {
1614 ll2("use table " << run(i).tabname);
1615 cnt++;
1616 }
1617 }
1618 ll0("selecttables: use " << cnt << "/" << maxrun() << " in this loop");
1619 }
1620
1621 static void
makeops()1622 makeops()
1623 {
1624 selecttables();
1625 for (uint i = 0; i < maxrun(); i++)
1626 if (! run(i).skip)
1627 makeops(run(i));
1628 ll0("makeops: used records = " << g_usedops);
1629 }
1630
1631 static int
addndbop(Run & r,Op * op)1632 addndbop(Run& r, Op* op)
1633 {
1634 chkdb((g_op = g_con->getNdbOperation(r.tabname)) != 0);
1635 switch (op->type) {
1636 case Op::INS:
1637 chkdb(g_op->insertTuple() == 0);
1638 break;
1639 case Op::DEL:
1640 chkdb(g_op->deleteTuple() == 0);
1641 break;
1642 case Op::UPD:
1643 chkdb(g_op->updateTuple() == 0);
1644 break;
1645 default:
1646 assert(false);
1647 break;
1648 }
1649 uint i;
1650 for (i = 0; i < ncol(); i++) {
1651 const Col& c = getcol(i);
1652 const Data& d = op->data[0];
1653 if (! c.pk)
1654 continue;
1655 chkdb(g_op->equal(c.name, (const char*)d.ptr[i].v) == 0);
1656 }
1657 if (op->type != Op::DEL) {
1658 for (i = 0; i < ncol(); i++) {
1659 const Col& c = getcol(i);
1660 const Data& d = op->data[0];
1661 if (c.pk)
1662 continue;
1663 if (d.noop & (1 << i))
1664 continue;
1665 assert(d.ind[i] >= 0);
1666 if (! c.isblob()) {
1667 if (d.ind[i] == 0)
1668 chkdb(g_op->setValue(c.name, (const char*)d.ptr[i].v) == 0);
1669 else
1670 chkdb(g_op->setValue(c.name, (const char*)0) == 0);
1671 } else {
1672 const Data::Txt& txt = *d.ptr[i].txt;
1673 g_bh = g_op->getBlobHandle(c.name);
1674 if (d.ind[i] == 0)
1675 chkdb(g_bh->setValue(txt.val, txt.len) == 0);
1676 else
1677 chkdb(g_bh->setValue(0, 0) == 0);
1678 g_bh = 0;
1679 }
1680 }
1681 }
1682 g_op = 0;
1683 return 0;
1684 }
1685
1686 static int
runops()1687 runops()
1688 {
1689 ll1("runops");
1690 Op* gci_op[g_maxtab][g_maxpk];
1691 uint left = 0; // number of table pks with ops
1692 Uint32 pk1;
1693 int i;
1694 for (i = 0; i < (int)maxrun(); i++) {
1695 Run& r = run(i);
1696 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
1697 gci_op[i][pk1] = 0;
1698 // total op on the pk
1699 Op* tot_op = r.pk_op[pk1];
1700 if (tot_op == 0)
1701 continue;
1702 if (tot_op->next_gci == 0) {
1703 assert(g_loop != 0 && tot_op->type == Op::INS);
1704 continue;
1705 }
1706 // first commit chain
1707 assert(tot_op->next_gci != 0);
1708 gci_op[i][pk1] = tot_op->next_gci;
1709 left++;
1710 }
1711 }
1712
1713 while (left != 0) {
1714 unsigned int i = urandom(maxrun());
1715 pk1 = urandom(g_opts.maxpk);
1716 if (gci_op[i][pk1] == 0)
1717 continue;
1718 Run& r = run(i);
1719 // do the ops in one transaction
1720 chkdb((g_con = g_ndb->startTransaction()) != 0);
1721 Op* com_op = gci_op[i][pk1]->next_com;
1722 assert(com_op != 0);
1723 // first op in chain
1724 Op* op = com_op->next_op;
1725 assert(op != 0);
1726 while (op != 0) {
1727 ll2("runops:" << *op);
1728 chkrc(addndbop(r, op) == 0);
1729 op = op->next_op;
1730 }
1731 chkdb(g_con->execute(Commit) == 0);
1732 Uint64 val;
1733 g_con->getGCI(&val);
1734 gci_op[i][pk1]->gci = com_op->gci = val;
1735 ll2("commit: " << run(i).tabname << " gci=" << com_op->gci);
1736 g_ndb->closeTransaction(g_con);
1737 g_con = 0;
1738 // next chain
1739 gci_op[i][pk1] = gci_op[i][pk1]->next_gci;
1740 if (gci_op[i][pk1] == 0) {
1741 assert(left != 0);
1742 left--;
1743 }
1744 }
1745 assert(left == 0);
1746 return 0;
1747 }
1748
1749 // move com chains with same gci under same gci entry
1750 static void
mergeops(Run & r)1751 mergeops(Run& r)
1752 {
1753 ll2("mergeops: " << r.tabname);
1754 uint mergecnt = 0;
1755 Uint32 pk1;
1756 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
1757 Op* tot_op = r.pk_op[pk1];
1758 if (tot_op == 0)
1759 continue;
1760 Op* gci_op = tot_op->next_gci;
1761 if (gci_op == 0) {
1762 assert(g_loop != 0 && tot_op->type == Op::INS);
1763 continue;
1764 }
1765 while (gci_op != 0) {
1766 Op* com_op = gci_op->next_com;
1767 assert(com_op != 0 && com_op->next_com == 0);
1768 assert(gci_op->gci == com_op->gci);
1769 Op* last_com = com_op;
1770 Op* gci_op2 = gci_op->next_gci;
1771 while (gci_op2 != 0 && gci_op->gci == gci_op2->gci) {
1772 // move link to com level
1773 last_com = last_com->next_com = gci_op2->next_com;
1774 // merge to gci
1775 reqrc(compop(gci_op, gci_op2, gci_op) == 0);
1776 // move to next and discard
1777 Op* tmp_op = gci_op2;
1778 gci_op2 = gci_op2->next_gci;
1779 freeop(tmp_op);
1780 mergecnt++;
1781 assert(r.gciops != 0 && g_gciops != 0);
1782 r.gciops--;
1783 g_gciops--;
1784 }
1785 gci_op = gci_op->next_gci = gci_op2;
1786 }
1787 }
1788 ll1("mergeops: " << r.tabname << ": gci recs = " << r.gciops);
1789 }
1790
1791 static void
mergeops()1792 mergeops()
1793 {
1794 for (uint i = 0; i < maxrun(); i++)
1795 mergeops(run(i));
1796 ll1("mergeops: used recs = " << g_usedops << " gci recs = " << g_gciops);
1797 }
1798
1799 // set bit for equal post/pre data in UPD, for use in event match
1800 static void
cmppostpre(Run & r)1801 cmppostpre(Run& r)
1802 {
1803 ll2("cmppostpre: " << r.tabname);
1804 Uint32 pk1;
1805 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
1806 Op* tot_op = r.pk_op[pk1];
1807 Op* gci_op = tot_op ? tot_op->next_gci : 0;
1808 while (gci_op != 0) {
1809 if (gci_op->type == Op::UPD) {
1810 Data (&d)[2] = gci_op->data;
1811 uint i;
1812 for (i = 0; i < ncol(); i++) {
1813 const Col& c = getcol(i);
1814 bool eq =
1815 (d[0].ind[i] == 1 && d[1].ind[i] == 1) ||
1816 (d[0].ind[i] == 0 && d[1].ind[i] == 0 && cmpcol(c, d[0], d[1])== 0);
1817 if (eq) {
1818 d[0].ppeq |= (1 << i);
1819 d[1].ppeq |= (1 << i);
1820 }
1821 }
1822 }
1823 gci_op = gci_op->next_gci;
1824 }
1825 }
1826 }
1827
1828 static void
cmppostpre()1829 cmppostpre()
1830 {
1831 ll1("cmppostpre");
1832 for (uint i = 0; i < maxrun(); i++)
1833 cmppostpre(run(i));
1834 }
1835
1836 static int
findevent(const NdbEventOperation * evt_op)1837 findevent(const NdbEventOperation* evt_op)
1838 {
1839 uint i;
1840 for (i = 0; i < maxrun(); i++) {
1841 if (run(i).evt_op == evt_op)
1842 break;
1843 }
1844 chkrc(i < maxrun());
1845 return i;
1846 }
1847
1848 static void
geteventdata(Run & r)1849 geteventdata(Run& r)
1850 {
1851 Data (&d)[2] = g_rec_ev->data;
1852 int i, j;
1853 for (j = 0; j < 2; j++) {
1854 for (i = 0; i < (int)ncol(); i++) {
1855 const Col& c = getcol(i);
1856 int ind, ret;
1857 if (! c.isblob()) {
1858 NdbRecAttr* ra = r.ev_ra[j][i];
1859 ind = ra->isNULL();
1860 } else {
1861 NdbBlob* bh = r.ev_bh[j][i];
1862 ret = bh->getDefined(ind);
1863 assert(ret == 0);
1864 if (ind == 0) { // value was returned and is not NULL
1865 Data::Txt& txt = *d[j].ptr[i].txt;
1866 Uint64 len64;
1867 ret = bh->getLength(len64);
1868 assert(ret == 0);
1869 txt.len = (uint)len64;
1870 delete [] txt.val;
1871 txt.val = new char [txt.len];
1872 memset(txt.val, 'X', txt.len);
1873 Uint32 len = txt.len;
1874 ret = bh->readData(txt.val, len);
1875 assert(ret == 0 && len == txt.len);
1876 }
1877 }
1878 d[j].ind[i] = ind;
1879 }
1880 }
1881 }
1882
1883 static int
addgcievents(Uint64 gci)1884 addgcievents(Uint64 gci)
1885 {
1886 ll1("getgcieventops");
1887 uint count = 0;
1888 uint seen_current = 0;
1889 Uint32 iter = 0;
1890 while (1) {
1891 Uint32 evtypes = 0;
1892 const NdbEventOperation* evt_op =
1893 g_ndb->getGCIEventOperations(&iter, &evtypes);
1894 if (evt_op == 0)
1895 break;
1896 // evt_op->getGCI() is not defined yet
1897 int i;
1898 chkrc((i = findevent(evt_op)) != -1);
1899 run(i).addevtypes(gci, evtypes, 0);
1900 seen_current += (g_evt_op == evt_op);
1901 count++;
1902 }
1903 chkrc(seen_current == 1);
1904 ll1("addgcievents: " << count);
1905 return 0;
1906 }
1907
1908 static int
runevents()1909 runevents()
1910 {
1911 ll1("runevents");
1912 uint mspoll = 1000;
1913 uint npoll = 6; // strangely long delay
1914 ll1("poll " << npoll);
1915 Uint64 gci = (Uint64)0;
1916 while (npoll != 0) {
1917 npoll--;
1918 int ret;
1919 ret = g_ndb->pollEvents(mspoll);
1920 if (ret <= 0)
1921 continue;
1922 while (1) {
1923 g_rec_ev->init(Op::EV);
1924 g_evt_op = g_ndb->nextEvent();
1925 if (g_evt_op == 0)
1926 break;
1927 Uint64 newgci = g_evt_op->getGCI();
1928 assert(newgci != 0);
1929 g_rec_ev->gci = newgci;
1930 if (gci != newgci) {
1931 ll1("new gci: " << gci << " -> " << newgci);
1932 gci = newgci;
1933 // add slot in each tab|e
1934 uint i;
1935 for (i = 0; i < maxtab(); i++)
1936 chkrc(run(i).addgci(gci) == 0);
1937 chkrc(addgcievents(gci) == 0);
1938 }
1939 int i;
1940 chkrc((i = findevent(g_evt_op)) != -1);
1941 Run& r = run(i);
1942 NdbDictionary::Event::TableEvent evtype = g_evt_op->getEventType();
1943 chkrc(seteventtype(g_rec_ev, evtype) == 0);
1944 r.addevtypes(gci, (Uint32)evtype, 1);
1945 geteventdata(r);
1946 ll2("runevents: EVT: " << *g_rec_ev);
1947 // check basic sanity
1948 Uint32 pk1 = ~(Uint32)0;
1949 chkrc(checkop(g_rec_ev, pk1) == 0);
1950 // add to events
1951 Op* tot_ev = r.pk_ev[pk1];
1952 if (tot_ev == 0)
1953 tot_ev = r.pk_ev[pk1] = getop(Op::EV);
1954 Op* last_ev = tot_ev;
1955 while (last_ev->next_ev != 0)
1956 last_ev = last_ev->next_ev;
1957 // copy and add
1958 Op* ev = getop(Op::EV);
1959 copyop(g_rec_ev, ev);
1960 g_rec_ev->freemem();
1961 last_ev->next_ev = ev;
1962 g_num_ev++;
1963 }
1964 }
1965 ll1("runevents: used ops = " << g_usedops << " events = " << g_num_ev);
1966 return 0;
1967 }
1968
1969 static int
cmpopevdata(const Data & d1,const Data & d2)1970 cmpopevdata(const Data& d1, const Data& d2)
1971 {
1972 uint i;
1973 for (i = 0; i < ncol(); i++) {
1974 const Col& c = getcol(i);
1975 if (cmpcol(c, d1, d2) != 0) {
1976 if ((d1.ppeq & (1 << i)) && d2.ind[i] == -1)
1977 ; // post/pre data equal and no event data returned is OK
1978 else
1979 return 1;
1980 }
1981 }
1982 return 0;
1983 }
1984
1985 // compare operation to event data
1986 static int
cmpopevdata(const Data (& d1)[2],const Data (& d2)[2])1987 cmpopevdata(const Data (&d1)[2], const Data (&d2)[2])
1988 {
1989 if (cmpopevdata(d1[0], d2[0]) != 0)
1990 return 1;
1991 if (cmpopevdata(d1[1], d2[1]) != 0)
1992 return 1;
1993 return 0;
1994 }
1995
1996 static int
matchevent(Run & r,Op * ev)1997 matchevent(Run& r, Op* ev)
1998 {
1999 Data (&d2)[2] = ev->data;
2000 // get PK
2001 Uint32 pk1 = d2[0].pk1;
2002 chkrc(pk1 < g_opts.maxpk);
2003 // on error repeat and print details
2004 uint loop = 0;
2005 while (loop <= 1) {
2006 int g_loglevel = loop == 0 ? g_opts.loglevel : 2;
2007 ll1("matchevent: " << r.tabname << ": pk1=" << pk1 << " type=" << ev->type);
2008 ll2("EVT: " << *ev);
2009 Op* tot_op = r.pk_op[pk1];
2010 Op* gci_op = tot_op ? tot_op->next_gci : 0;
2011 uint pos = 0;
2012 bool ok = false;
2013 while (gci_op != 0) {
2014 ll2("GCI: " << *gci_op);
2015 // print details
2016 Op* com_op = gci_op->next_com;
2017 assert(com_op != 0);
2018 while (com_op != 0) {
2019 ll2("COM: " << *com_op);
2020 Op* op = com_op->next_op;
2021 assert(op != 0);
2022 while (op != 0) {
2023 ll2("OP : " << *op);
2024 op = op->next_op;
2025 }
2026 com_op = com_op->next_com;
2027 }
2028 // match against GCI op
2029 if (gci_op->type != Op::NUL) {
2030 const Data (&d1)[2] = gci_op->data;
2031 if (cmpopevdata(d1, d2) == 0) {
2032 bool tmpok = true;
2033 if (gci_op->type != ev->type) {
2034 ll2("***: wrong type " << gci_op->type << " != " << ev->type);
2035 tmpok = false;
2036 }
2037 if (gci_op->match) {
2038 ll2("***: duplicate match");
2039 tmpok = false;
2040 }
2041 if (pos != r.ev_pos[pk1]) {
2042 ll2("***: wrong pos " << pos << " != " << r.ev_pos[pk1]);
2043 tmpok = false;
2044 }
2045 if (gci_op->gci != ev->gci) {
2046 ll2("***: wrong gci " << gci_op->gci << " != " << ev->gci);
2047 tmpok = false;
2048 }
2049 if (tmpok) {
2050 ok = gci_op->match = true;
2051 ll2("match");
2052 }
2053 }
2054 pos++;
2055 }
2056 gci_op = gci_op->next_gci;
2057 }
2058 if (ok) {
2059 ll2("matchevent: match");
2060 return 0;
2061 }
2062 ll0("matchevent: ERROR: no match");
2063 if (g_loglevel >= 2)
2064 return -1;
2065 loop++;
2066 }
2067 return 0;
2068 }
2069
2070 static int
matchevents(Run & r)2071 matchevents(Run& r)
2072 {
2073 ll1("matchevents: " << r.tabname);
2074 uint nomatch = 0;
2075 Uint32 pk1;
2076 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
2077 Op* tot_ev = r.pk_ev[pk1];
2078 if (tot_ev == 0)
2079 continue;
2080 Op* ev = tot_ev->next_ev;
2081 while (ev != 0) {
2082 if (matchevent(r, ev) < 0)
2083 nomatch++;
2084 r.ev_pos[pk1]++;
2085 ev = ev->next_ev;
2086 }
2087 }
2088 chkrc(nomatch == 0);
2089 return 0;
2090 }
2091
2092 static int
matchevents()2093 matchevents()
2094 {
2095 ll1("matchevents");
2096 for (uint i = 0; i < maxrun(); i++)
2097 chkrc(matchevents(run(i)) == 0);
2098 return 0;
2099 }
2100
2101 static int
matchops(Run & r)2102 matchops(Run& r)
2103 {
2104 ll1("matchops: " << r.tabname);
2105 uint nomatch = 0;
2106 Uint32 pk1;
2107 for (pk1 = 0; pk1 < g_opts.maxpk; pk1++) {
2108 Op* tot_op = r.pk_op[pk1];
2109 if (tot_op == 0)
2110 continue;
2111 Op* gci_op = tot_op->next_gci;
2112 while (gci_op != 0) {
2113 if (gci_op->type == Op::NUL) {
2114 ll2("GCI: " << *gci_op << " [skip NUL]");
2115 } else if (gci_op->match) {
2116 ll2("GCI: " << *gci_op << " [match OK]");
2117 } else {
2118 ll0("GCI: " << *gci_op);
2119 Op* com_op = gci_op->next_com;
2120 assert(com_op != 0);
2121 ll0("COM: " << *com_op);
2122 Op* op = com_op->next_op;
2123 assert(op != 0);
2124 while (op != 0) {
2125 ll0("OP : " << *op);
2126 op = op->next_op;
2127 }
2128 ll0("no matching event");
2129 nomatch++;
2130 }
2131 gci_op = gci_op->next_gci;
2132 }
2133 }
2134 chkrc(nomatch == 0);
2135 return 0;
2136 }
2137
2138 static int
matchops()2139 matchops()
2140 {
2141 ll1("matchops");
2142 for (uint i = 0; i < maxrun(); i++)
2143 chkrc(matchops(run(i)) == 0);
2144 return 0;
2145 }
2146
2147 static int
matchgcievents(Run & r)2148 matchgcievents(Run& r)
2149 {
2150 ll1("matchgcievents: " << r.tabname);
2151 uint i;
2152 for (i = 0; i < r.gcicnt; i++) {
2153 Uint32 t0 = r.gcievtypes[i][0];
2154 Uint32 t1 = r.gcievtypes[i][1];
2155 ll1("gci: " << r.gcinum[i] << hex << " report: " << t0 << " seen: " << t1);
2156
2157 if (r.skip)
2158 chkrc(t0 == 0 && t1 == 0);
2159 if (t0 == 0 && t1 == 0)
2160 continue;
2161
2162 // check if not reported event op seen
2163 chkrc(t0 != 0);
2164 // check if not reported event type seen
2165 chkrc((~t0 & t1) == 0);
2166
2167 // the other way does not work under merge
2168 if (g_opts.separate_events) {
2169 // check if reported event op not seen
2170 chkrc(t1 != 0);
2171 // check if reported event type not seen
2172 chkrc((t0 & ~t1) == 0);
2173 }
2174 }
2175 return 0;
2176 }
2177
2178 static int
matchgcievents()2179 matchgcievents()
2180 {
2181 ll1("matchgcievents");
2182 for (uint i = 0; i < maxrun(); i++)
2183 chkrc(matchgcievents(run(i)) == 0);
2184 return 0;
2185 }
2186
2187 static void
setseed(int n)2188 setseed(int n)
2189 {
2190 uint seed;
2191 if (n == -1) {
2192 if (g_opts.seed == 0)
2193 return;
2194 if (g_opts.seed != (uint)-1)
2195 seed = (uint)g_opts.seed;
2196 else
2197 seed = 1 + (ushort)getpid();
2198 } else {
2199 if (g_opts.seed != 0)
2200 return;
2201 seed = n;
2202 }
2203 ll0("seed=" << seed);
2204 ndb_srand(seed);
2205 }
2206
2207 static int
runtest()2208 runtest()
2209 {
2210 setseed(-1);
2211 initrun();
2212 chkrc(createtables() == 0);
2213 chkrc(createevents() == 0);
2214 for (g_loop = 0; g_opts.loop == 0 || g_loop < g_opts.loop; g_loop++) {
2215 ll0("=== loop " << g_loop << " ===");
2216 setseed(g_loop);
2217 resetmem();
2218 chkrc(scantable() == 0); // alternative: save tot_op for loop > 0
2219 makeops();
2220 g_rec_ev = getop(Op::EV);
2221 chkrc(createeventop() == 0);
2222 chkrc(executeeventop() == 0);
2223 chkrc(waitgci(3) == 0);
2224 chkrc(runops() == 0);
2225 if (! g_opts.separate_events)
2226 mergeops();
2227 cmppostpre();
2228 chkrc(runevents() == 0);
2229 ll0("counts: gci ops = " << g_gciops << " ev ops = " << g_num_ev);
2230 chkrc(matchevents() == 0);
2231 chkrc(matchops() == 0);
2232 chkrc(matchgcievents() == 0);
2233 chkrc(dropeventops() == 0);
2234 // time erases everything..
2235 chkrc(waitgci(1) == 0);
2236 }
2237 chkrc(dropevents() == 0);
2238 chkrc(droptables() == 0);
2239 resetmem();
2240 deleteops();
2241 return 0;
2242 }
2243
2244 static struct my_option
2245 my_long_options[] =
2246 {
2247 NDB_STD_OPTS("test_event_merge"),
2248 { "abort-on-error", NDB_OPT_NOSHORT, "Do abort() on any error",
2249 (uchar **)&g_opts.abort_on_error, (uchar **)&g_opts.abort_on_error, 0,
2250 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2251 { "loglevel", NDB_OPT_NOSHORT, "Logging level in this program 0-3 (default 0)",
2252 (uchar **)&g_opts.loglevel, (uchar **)&g_opts.loglevel, 0,
2253 GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2254 { "loop", NDB_OPT_NOSHORT, "Number of test loops (default 5, 0=forever)",
2255 (uchar **)&g_opts.loop, (uchar **)&g_opts.loop, 0,
2256 GET_INT, REQUIRED_ARG, 5, 0, 0, 0, 0, 0 },
2257 { "maxops", NDB_OPT_NOSHORT, "Approx number of PK operations per table (default 1000)",
2258 (uchar **)&g_opts.maxops, (uchar **)&g_opts.maxops, 0,
2259 GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 },
2260 { "maxpk", NDB_OPT_NOSHORT, "Number of different PK values (default 10, max 1000)",
2261 (uchar **)&g_opts.maxpk, (uchar **)&g_opts.maxpk, 0,
2262 GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
2263 { "maxtab", NDB_OPT_NOSHORT, "Number of tables (default 10, max 100)",
2264 (uchar **)&g_opts.maxtab, (uchar **)&g_opts.maxtab, 0,
2265 GET_INT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
2266 { "no-blobs", NDB_OPT_NOSHORT, "Omit blob attributes (5.0: true)",
2267 (uchar **)&g_opts.no_blobs, (uchar **)&g_opts.no_blobs, 0,
2268 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2269 { "no-implicit-nulls", NDB_OPT_NOSHORT, "Insert must include all attrs"
2270 " i.e. no implicit NULLs",
2271 (uchar **)&g_opts.no_implicit_nulls, (uchar **)&g_opts.no_implicit_nulls, 0,
2272 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2273 { "no-missing-update", NDB_OPT_NOSHORT, "Update must include all non-PK attrs",
2274 (uchar **)&g_opts.no_missing_update, (uchar **)&g_opts.no_missing_update, 0,
2275 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2276 { "no-multiops", NDB_OPT_NOSHORT, "Allow only 1 operation per commit",
2277 (uchar **)&g_opts.no_multiops, (uchar **)&g_opts.no_multiops, 0,
2278 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2279 { "no-nulls", NDB_OPT_NOSHORT, "Create no NULL values",
2280 (uchar **)&g_opts.no_nulls, (uchar **)&g_opts.no_nulls, 0,
2281 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2282 { "one-blob", NDB_OPT_NOSHORT, "Only one blob attribute (default 2)",
2283 (uchar **)&g_opts.one_blob, (uchar **)&g_opts.one_blob, 0,
2284 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2285 { "opstring", NDB_OPT_NOSHORT, "Operations to run e.g. idiucdc (c is commit) or"
2286 " iuuc:uudc (the : separates loops)",
2287 (uchar **)&g_opts.opstring, (uchar **)&g_opts.opstring, 0,
2288 GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2289 { "seed", NDB_OPT_NOSHORT, "Random seed (0=loop number, default -1=random)",
2290 (uchar **)&g_opts.seed, (uchar **)&g_opts.seed, 0,
2291 GET_INT, REQUIRED_ARG, -1, 0, 0, 0, 0, 0 },
2292 { "separate-events", NDB_OPT_NOSHORT, "Do not combine events per GCI (5.0: true)",
2293 (uchar **)&g_opts.separate_events, (uchar **)&g_opts.separate_events, 0,
2294 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2295 { "tweak", NDB_OPT_NOSHORT, "Whatever the source says",
2296 (uchar **)&g_opts.tweak, (uchar **)&g_opts.tweak, 0,
2297 GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2298 { "use-table", NDB_OPT_NOSHORT, "Use existing tables",
2299 (uchar **)&g_opts.use_table, (uchar **)&g_opts.use_table, 0,
2300 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2301 { "blob-version", NDB_OPT_NOSHORT, "Blob version 1 or 2 (default 2)",
2302 (uchar**)&g_opts.blob_version, (uchar**)&g_opts.blob_version, 0,
2303 GET_INT, REQUIRED_ARG, 2, 0, 0, 0, 0, 0 },
2304 { 0, 0, 0,
2305 0, 0, 0,
2306 GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
2307 };
2308
2309
2310 static int
checkopts()2311 checkopts()
2312 {
2313 if (g_opts.separate_events) {
2314 g_opts.no_blobs = true;
2315 }
2316 if (g_opts.no_multiops) {
2317 g_maxcom = 1;
2318 }
2319 if (g_opts.opstring != 0) {
2320 uint len = strlen(g_opts.opstring);
2321 char* str = new char [len + 1];
2322 memcpy(str, g_opts.opstring, len + 1);
2323 char* s = str;
2324 while (1) {
2325 g_opstringpart[g_opstringparts++] = s;
2326 s = strchr(s, ':');
2327 if (s == 0)
2328 break;
2329 *s++ = 0;
2330 }
2331 uint i;
2332 for (i = 0; i < g_opstringparts; i++) {
2333 const char* s = g_opstringpart[i];
2334 while (*s != 0) {
2335 if (strchr("iduc", *s++) == 0) {
2336 ll0("opstring chars are i,d,u,c");
2337 return -1;
2338 }
2339 }
2340 if (s == g_opstringpart[i] || s[-1] != 'c') {
2341 ll0("opstring chain must end in 'c'");
2342 return -1;
2343 }
2344 }
2345 }
2346 if (g_opts.no_nulls) {
2347 g_opts.no_implicit_nulls = true;
2348 }
2349 if (g_opts.maxpk > g_maxpk ||
2350 g_opts.maxtab > (int)g_maxtab) {
2351 return -1;
2352 }
2353 if (g_opts.blob_version < 1 || g_opts.blob_version > 2) {
2354 return -1;
2355 }
2356 return 0;
2357 }
2358
2359 static int
doconnect()2360 doconnect()
2361 {
2362 g_ncc = new Ndb_cluster_connection();
2363 chkdb(g_ncc->connect(30) == 0);
2364 g_ndb = new Ndb(g_ncc, "TEST_DB");
2365 chkdb(g_ndb->init() == 0 && g_ndb->waitUntilReady(30) == 0);
2366 return 0;
2367 }
2368
2369 int
main(int argc,char ** argv)2370 main(int argc, char** argv)
2371 {
2372 ndb_init();
2373 const char* progname =
2374 strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
2375 ndbout << progname;
2376 for (int i = 1; i < argc; i++)
2377 ndbout << " " << argv[i];
2378 ndbout << endl;
2379 int ret;
2380 ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
2381 if (ret != 0 || argc != 0 || checkopts() != 0)
2382 return NDBT_ProgramExit(NDBT_WRONGARGS);
2383 if (doconnect() == 0 && runtest() == 0) {
2384 delete g_ndb;
2385 delete g_ncc;
2386 return NDBT_ProgramExit(NDBT_OK);
2387 }
2388 dropeventops(true);
2389 dropevents(true);
2390 delete g_ndb;
2391 delete g_ncc;
2392 return NDBT_ProgramExit(NDBT_FAILED);
2393 }
2394