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