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