MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
testIndexStat.cpp
1 /*
2  Copyright (c) 2006, 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 <NdbIndexStat.hpp>
22 #include <NdbTest.hpp>
23 #include <ndb_version.h>
24 #include <NDBT_Stats.hpp>
25 #include <math.h>
26 
27 #undef min
28 #undef max
29 #define min(a, b) ((a) <= (b) ? (a) : (b))
30 #define max(a, b) ((a) >= (b) ? (a) : (b))
31 
32 inline NdbOut&
33 NdbOut::operator<<(double x)
34 {
35  char buf[100];
36  sprintf(buf, "%.2f", x);
37  *this << buf;
38  return *this;
39 }
40 
41 struct Opts {
42  int loglevel;
43  uint seed;
44  uint attrs;
45  uint loops;
46  uint rows;
47  uint ops;
48  uint nullkeys;
49  uint rpk;
50  uint rpkvar;
51  uint scanpct;
52  uint eqscans;
53  my_bool keeptable;
54  my_bool abort;
55  const char* dump;
56  Opts() :
57  loglevel(0),
58  seed(0),
59  attrs(3),
60  loops(1),
61  rows(10000),
62  ops(100),
63  nullkeys(10),
64  rpk(10),
65  rpkvar(10),
66  scanpct(10),
67  eqscans(30),
68  keeptable(false),
69  abort(false),
70  dump(0)
71  {}
72 };
73 
74 static Opts g_opts;
75 static uint g_loop = 0;
76 
77 static const char* g_tabname = "ts1";
78 static const char* g_indname = "ts1x1";
79 static const uint g_numattrs = 3;
80 static const uint g_charlen = 10;
81 static const char* g_csname = "latin1_swedish_ci";
82 static CHARSET_INFO* g_cs;
83 
84 // keys nullability
85 static const bool g_b_nullable = true;
86 static const bool g_c_nullable = true;
87 static const bool g_d_nullable = true;
88 
89 // value limits
90 struct Lim {
91  bool all_nullable;
92  uint b_min;
93  uint b_max;
94  const char* c_char;
95  uint d_min;
96  uint d_max;
97 };
98 
99 static Lim g_lim_val;
100 static Lim g_lim_bnd;
101 
102 static Ndb_cluster_connection* g_ncc = 0;
103 static Ndb* g_ndb = 0;
104 static Ndb* g_ndb_sys = 0;
105 static NdbDictionary::Dictionary* g_dic = 0;
106 static const NdbDictionary::Table* g_tab = 0;
107 static const NdbDictionary::Index* g_ind = 0;
108 static const NdbRecord* g_tab_rec = 0;
109 static const NdbRecord* g_ind_rec = 0;
110 
111 struct my_record
112 {
113  Uint8 m_null_bm;
114  Uint8 fill[3];
115  Uint32 m_a;
116  Uint32 m_b;
117  char m_c[1+g_charlen];
118  Uint16 m_d;
119 };
120 
121 static const Uint32 g_ndbrec_a_offset=offsetof(my_record, m_a);
122 static const Uint32 g_ndbrec_b_offset=offsetof(my_record, m_b);
123 static const Uint32 g_ndbrec_b_nb_offset=1;
124 static const Uint32 g_ndbrec_c_offset=offsetof(my_record, m_c);
125 static const Uint32 g_ndbrec_c_nb_offset=2;
126 static const Uint32 g_ndbrec_d_offset=offsetof(my_record, m_d);
127 static const Uint32 g_ndbrec_d_nb_offset=3;
128 static const Uint32 g_ndbrecord_bytes=sizeof(my_record);
129 
130 static NdbTransaction* g_con = 0;
131 static NdbOperation* g_op = 0;
132 static NdbScanOperation* g_scan_op = 0;
133 static NdbIndexScanOperation* g_rangescan_op = 0;
134 
135 static NdbIndexStat* g_is = 0;
136 static bool g_has_created_stat_tables = false;
137 static bool g_has_created_stat_events = false;
138 
139 static uint
140 urandom()
141 {
142  uint r = (uint)random();
143  return r;
144 }
145 
146 static uint
147 urandom(uint m)
148 {
149  if (m == 0)
150  return 0;
151  uint r = urandom();
152  r = r % m;
153  return r;
154 }
155 
156 static int& g_loglevel = g_opts.loglevel; // default log level
157 
158 #define chkdb(x) \
159  do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; errdb(); if (g_opts.abort) abort(); return -1; } while (0)
160 
161 #define chker(x) \
162  do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; ndbout << "errno: " << errno; if (g_opts.abort) abort(); return -1; } while (0)
163 
164 #define chkrc(x) \
165  do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; if (g_opts.abort) abort(); return -1; } while (0)
166 
167 #define llx(n, x) \
168  do { if (likely(g_loglevel < n)) break; ndbout << x << endl; } while (0)
169 
170 #define ll0(x) llx(0, x)
171 #define ll1(x) llx(1, x)
172 #define ll2(x) llx(2, x)
173 #define ll3(x) llx(3, x)
174 
175 static void
176 errdb()
177 {
178  uint any = 0;
179  if (g_ncc != 0) {
180  NdbError e;
181  e.code = g_ncc->get_latest_error();
182  e.message = g_ncc->get_latest_error_msg();
183  if (e.code != 0)
184  ll0(++any << " ncc: error" << e);
185  }
186  if (g_ndb != 0) {
187  const NdbError& e = g_ndb->getNdbError();
188  if (e.code != 0)
189  ll0(++any << " ndb: error " << e);
190  }
191  if (g_dic != 0) {
192  const NdbError& e = g_dic->getNdbError();
193  if (e.code != 0)
194  ll0(++any << " dic: error " << e);
195  }
196  if (g_con != 0) {
197  const NdbError& e = g_con->getNdbError();
198  if (e.code != 0)
199  ll0(++any << " con: error " << e);
200  }
201  if (g_op != 0) {
202  const NdbError& e = g_op->getNdbError();
203  if (e.code != 0)
204  ll0(++any << " op: error " << e);
205  }
206  if (g_scan_op != 0) {
207  const NdbError& e = g_scan_op->getNdbError();
208  if (e.code != 0)
209  ll0(++any << " scan_op: error " << e);
210  }
211  if (g_rangescan_op != 0) {
212  const NdbError& e = g_rangescan_op->getNdbError();
213  if (e.code != 0)
214  ll0(++any << " rangescan_op: error " << e);
215  }
216  if (g_is != 0) {
217  const NdbIndexStat::Error& e = g_is->getNdbError();
218  if (e.code != 0)
219  ll0(++any << " stat: error " << e);
220  }
221  if (! any)
222  ll0("unknown db error");
223 }
224 
225 /* Methods to create NdbRecord structs for the table and index */
226 static int
227 createNdbRecords()
228 {
229  ll1("createNdbRecords");
230  const Uint32 numCols=4;
231  const Uint32 numIndexCols=3;
232  NdbDictionary::RecordSpecification recSpec[numCols];
233 
234  recSpec[0].column= g_tab->getColumn("a"); // 4 bytes
235  recSpec[0].offset= g_ndbrec_a_offset;
236  recSpec[0].nullbit_byte_offset= ~(Uint32)0;
237  recSpec[0].nullbit_bit_in_byte= ~(Uint32)0;
238 
239  recSpec[1].column= g_tab->getColumn("b"); // 4 bytes
240  recSpec[1].offset= g_ndbrec_b_offset;
241  if (g_b_nullable) {
242  recSpec[1].nullbit_byte_offset= 0;
243  recSpec[1].nullbit_bit_in_byte= g_ndbrec_b_nb_offset;
244  } else {
245  recSpec[1].nullbit_byte_offset= ~(Uint32)0;
246  recSpec[1].nullbit_bit_in_byte= ~(Uint32)0;
247  }
248 
249  recSpec[2].column= g_tab->getColumn("c"); // Varchar(10) -> ~12 bytes
250  recSpec[2].offset= g_ndbrec_c_offset;
251  if (g_c_nullable) {
252  recSpec[2].nullbit_byte_offset= 0;
253  recSpec[2].nullbit_bit_in_byte= g_ndbrec_c_nb_offset;
254  } else {
255  recSpec[2].nullbit_byte_offset= ~(Uint32)0;
256  recSpec[2].nullbit_bit_in_byte= ~(Uint32)0;
257  }
258 
259  recSpec[3].column= g_tab->getColumn("d"); // 2 bytes
260  recSpec[3].offset= g_ndbrec_d_offset;
261  if (g_d_nullable) {
262  recSpec[3].nullbit_byte_offset= 0;
263  recSpec[3].nullbit_bit_in_byte= g_ndbrec_d_nb_offset;
264  } else {
265  recSpec[3].nullbit_byte_offset= ~(Uint32)0;
266  recSpec[3].nullbit_bit_in_byte= ~(Uint32)0;
267  }
268 
269  g_dic = g_ndb->getDictionary();
270  g_tab_rec= g_dic->createRecord(g_tab,
271  &recSpec[0],
272  numCols,
274  0);
275 
276  chkdb(g_tab_rec != NULL);
277 
278  g_ind_rec= g_dic->createRecord(g_ind,
279  &recSpec[1],
280  numIndexCols,
282  0);
283 
284  chkdb(g_ind_rec != NULL);
285  g_dic = 0;
286 
287  return 0;
288 }
289 
290 // create table ts0 (
291 // a int unsigned,
292 // b int unsigned, c varchar(10), d smallint unsigned,
293 // primary key using hash (a), index (b, c, d) )
294 
295 static int
296 createtable()
297 {
298  ll1("createtable");
299  NdbDictionary::Table tab(g_tabname);
300  tab.setLogging(false);
301  {
302  NdbDictionary::Column col("a");
303  col.setType(NdbDictionary::Column::Unsigned);
304  col.setPrimaryKey(true);
305  tab.addColumn(col);
306  }
307  {
308  NdbDictionary::Column col("b");
309  col.setType(NdbDictionary::Column::Unsigned);
310  col.setNullable(g_b_nullable);
311  tab.addColumn(col);
312  }
313  {
314  NdbDictionary::Column col("c");
315  col.setType(NdbDictionary::Column::Varchar);
316  col.setLength(g_charlen);
317  col.setCharset(g_cs);
318  col.setNullable(g_c_nullable);
319  tab.addColumn(col);
320  }
321  {
322  NdbDictionary::Column col("d");
324  col.setNullable(g_d_nullable);
325  tab.addColumn(col);
326  }
327 
328  g_dic = g_ndb->getDictionary();
329  if (g_dic->getTable(g_tabname) != 0)
330  chkdb(g_dic->dropTable(g_tabname) == 0);
331  chkdb(g_dic->createTable(tab) == 0);
332  chkdb((g_tab = g_dic->getTable(g_tabname)) != 0);
333  g_dic = 0;
334  return 0;
335 }
336 
337 static int
338 createindex()
339 {
340  ll1("createindex");
341  NdbDictionary::Index ind(g_indname);
342  ind.setTable(g_tabname);
344  ind.setLogging(false);
345  ind.addColumnName("b");
346  ind.addColumnName("c");
347  ind.addColumnName("d");
348 
349  g_dic = g_ndb->getDictionary();
350  chkdb(g_dic->createIndex(ind) == 0);
351  chkdb((g_ind = g_dic->getIndex(g_indname, g_tabname)) != 0);
352  g_dic = 0;
353  return 0;
354 }
355 
356 static int
357 droptable()
358 {
359  ll1("droptable");
360  g_dic = g_ndb->getDictionary();
361  chkdb(g_dic->dropTable(g_tabname) == 0);
362  g_dic = 0;
363  return 0;
364 }
365 
366 // values for keys and bounds
367 
368 struct Val {
369  uint8 m_numattrs;
370  int8 b_null;
371  int8 c_null;
372  int8 d_null;
373  Uint32 b;
374  uchar c[1 + g_charlen];
375  Uint16 d;
376  Val();
377  void init();
378  void copy(const Val& val2);
379  void make(uint numattrs, const Lim& lim);
380  int cmp(const Val& val2, uint numattrs = g_numattrs, uint* num_eq = 0) const;
381  void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
382 
383 private:
384  Val& operator=(const Val&);
385  Val(const Val&);
386 };
387 
388 static NdbOut&
389 operator<<(NdbOut& out, const Val& val)
390 {
391  out << "[";
392  if (val.m_numattrs >= 1) {
393  if (val.b_null)
394  out << "NULL";
395  else
396  out << val.b;
397  }
398  if (val.m_numattrs >= 2) {
399  out << " ";
400  if (val.c_null)
401  out << "NULL";
402  else {
403  char buf[1 + g_charlen];
404  sprintf(buf, "%.*s", val.c[0], &val.c[1]);
405  out << "'" << buf << "'";
406  }
407  }
408  if (val.m_numattrs >= 3) {
409  out << " ";
410  if (val.d_null)
411  out <<" NULL";
412  else
413  out << val.d;
414  }
415  out << "]";
416  return out;
417 }
418 
419 Val::Val()
420 {
421  init();
422 }
423 
424 void
425 Val::init()
426 {
427  m_numattrs = 0;
428  // junk rest
429  b_null = -1;
430  c_null = -1;
431  d_null = -1;
432  b = ~(Uint32)0;
433  memset(c, 0xff, sizeof(c));
434  d = ~(Uint16)0;
435 }
436 
437 void
438 Val::copy(const Val& val2)
439 {
440  require(this != &val2);
441  init();
442  m_numattrs = val2.m_numattrs;
443  if (m_numattrs >= 1) {
444  require(val2.b_null == 0 || val2.b_null == 1);
445  b_null = val2.b_null;
446  if (!b_null)
447  b = val2.b;
448  }
449  if (m_numattrs >= 2) {
450  require(val2.c_null == 0 || val2.c_null == 1);
451  c_null = val2.c_null;
452  if (!c_null)
453  memcpy(c, val2.c, sizeof(c));
454  }
455  if (m_numattrs >= 3) {
456  require(val2.d_null == 0 || val2.d_null == 1);
457  d_null = val2.d_null;
458  if (!d_null)
459  d = val2.d;
460  }
461 }
462 
463 void
464 Val::make(uint numattrs, const Lim& lim)
465 {
466  require(numattrs <= g_numattrs);
467  if (numattrs >= 1) {
468  const bool nullable = g_b_nullable || lim.all_nullable;
469  if (nullable && urandom(100) < g_opts.nullkeys)
470  b_null = 1;
471  else {
472  require(lim.b_min <= lim.b_max);
473  b = lim.b_min + urandom(lim.b_max - lim.b_min + 1);
474  b_null = 0;
475  }
476  }
477  if (numattrs >= 2) {
478  const bool nullable = g_c_nullable || lim.all_nullable;
479  if (nullable && urandom(100) < g_opts.nullkeys)
480  c_null = 1;
481  else {
482  // prefer shorter
483  const uint len = urandom(urandom(g_charlen + 1) + 1);
484  c[0] = len;
485  for (uint j = 0; j < len; j++) {
486  uint k = urandom(strlen(lim.c_char));
487  c[1 + j] = lim.c_char[k];
488  }
489  c_null = 0;
490  }
491  }
492  if (numattrs >= 3) {
493  const bool nullable = g_d_nullable || lim.all_nullable;
494  if (nullable && urandom(100) < g_opts.nullkeys)
495  d_null = 1;
496  else {
497  require(lim.d_min <= lim.d_max);
498  d = lim.d_min + urandom(lim.d_max - lim.d_min + 1);
499  d_null = 0;
500  }
501  }
502  m_numattrs = numattrs;
503 }
504 
505 int
506 Val::cmp(const Val& val2, uint numattrs, uint* num_eq) const
507 {
508  require(numattrs <= m_numattrs);
509  require(numattrs <= val2.m_numattrs);
510  uint n = 0; // attr index where differs
511  uint k = 0;
512  if (k == 0 && numattrs >= 1) {
513  if (! b_null && ! val2.b_null) {
514  if (b < val2.b)
515  k = -1;
516  else if (b > val2.b)
517  k = +1;
518  } else if (! b_null) {
519  k = +1;
520  } else if (! val2.b_null) {
521  k = -1;
522  }
523  if (k == 0)
524  n++;
525  }
526  if (k == 0 && numattrs >= 2) {
527  if (! c_null && ! val2.c_null) {
528  const uchar* s1 = &c[1];
529  const uchar* s2 = &val2.c[1];
530  const uint l1 = (uint)c[0];
531  const uint l2 = (uint)val2.c[0];
532  assert(l1 <= g_charlen && l2 <= g_charlen);
533  k = g_cs->coll->strnncollsp(g_cs, s1, l1, s2, l2, 0);
534  } else if (! c_null) {
535  k = +1;
536  } else if (! val2.c_null) {
537  k = -1;
538  }
539  if (k == 0)
540  n++;
541  }
542  if (k == 0 && numattrs >= 3) {
543  if (! d_null && ! val2.d_null) {
544  if (d < val2.d)
545  k = -1;
546  else if (d > val2.d)
547  k = +1;
548  } else if (! d_null) {
549  k = +1;
550  } else if (! val2.d_null) {
551  k = -1;
552  }
553  if (k == 0)
554  n++;
555  }
556  require(n <= numattrs);
557  if (num_eq != 0)
558  *num_eq = n;
559  return k;
560 }
561 
562 void
563 Val::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
564 {
565  const char* key = (j == 0 ? ib.low_key : ib.high_key);
566  const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
567  const Uint8 nullbits = *(const Uint8*)key;
568  require(numattrs <= g_numattrs);
569  if (numattrs >= 1) {
570  if (nullbits & (1 << g_ndbrec_b_nb_offset))
571  b_null = 1;
572  else {
573  memcpy(&b, &key[g_ndbrec_b_offset], sizeof(b));
574  b_null = 0;
575  }
576  }
577  if (numattrs >= 2) {
578  if (nullbits & (1 << g_ndbrec_c_nb_offset))
579  c_null = 1;
580  else {
581  memcpy(c, &key[g_ndbrec_c_offset], sizeof(c));
582  c_null = 0;
583  }
584  }
585  if (numattrs >= 3) {
586  if (nullbits & (1 << g_ndbrec_d_nb_offset))
587  d_null = 1;
588  else {
589  memcpy(&d, &key[g_ndbrec_d_offset], sizeof(d));
590  d_null = 0;
591  }
592  }
593  m_numattrs = numattrs;
594 }
595 
596 // index keys
597 
598 struct Key {
599  Val m_val;
600  int8 m_flag; // temp use
601  Key();
602 
603 private:
604  Key& operator=(const Key&);
605  Key(const Key&);
606 };
607 
608 static NdbOut&
609 operator<<(NdbOut& out, const Key& key)
610 {
611  out << key.m_val;
612  if (key.m_flag != -1)
613  out << " flag: " << key.m_flag;
614  return out;
615 }
616 
617 Key::Key()
618 {
619  m_flag = -1;
620 }
621 
622 static Key* g_keys = 0;
623 static uint* g_sortkeys = 0;
624 
625 static void
626 freekeys()
627 {
628  delete [] g_keys;
629  delete [] g_sortkeys;
630  g_keys = 0;
631  g_sortkeys = 0;
632 }
633 
634 static void
635 allockeys()
636 {
637  freekeys();
638  g_keys = new Key [g_opts.rows];
639  g_sortkeys = new uint [g_opts.rows];
640  require(g_keys != 0 && g_sortkeys != 0);
641  memset(g_sortkeys, 0xff, sizeof(uint) * g_opts.rows);
642 }
643 
644 static int
645 cmpkeys(const void* p1, const void* p2)
646 {
647  const uint i1 = *(const uint*)p1;
648  const uint i2 = *(const uint*)p2;
649  require(i1 < g_opts.rows && i2 < g_opts.rows);
650  const Key& key1 = g_keys[i1];
651  const Key& key2 = g_keys[i2];
652  const int k = key1.m_val.cmp(key2.m_val, g_opts.attrs);
653  return k;
654 }
655 
656 static void
657 sortkeys()
658 {
659  ll2("sortkeys");
660  uint i;
661 
662  // sort
663  for (i = 0; i < g_opts.rows; i++)
664  g_sortkeys[i] = i;
665  qsort(g_sortkeys, g_opts.rows, sizeof(uint), cmpkeys);
666 
667  // verify
668  uint unique = 1;
669  for (i = 1; i < g_opts.rows; i++) {
670  const uint i1 = g_sortkeys[i - 1];
671  const uint i2 = g_sortkeys[i];
672  require(i1 < g_opts.rows && i2 < g_opts.rows);
673  const Key& key1 = g_keys[i1];
674  const Key& key2 = g_keys[i2];
675  const int k = key1.m_val.cmp(key2.m_val, g_opts.attrs);
676  require(k <= 0);
677  if (k < 0)
678  unique++;
679  }
680 
681  // show min max key
682  ll1("minkey:" << g_keys[g_sortkeys[0]]);
683  ll1("maxkey:" << g_keys[g_sortkeys[g_opts.rows - 1]]);
684  ll1("unique:" << unique);
685 }
686 
687 static void
688 makekeys()
689 {
690  ll1("makekeys");
691 
692  uint initrows = g_opts.rows / g_opts.rpk;
693  require(initrows != 0);
694 
695  // distinct keys
696  uint i = 0;
697  while (i < initrows) {
698  Key& key = g_keys[i];
699  key.m_val.make(g_numattrs, g_lim_val);
700  i++;
701  }
702 
703  // remaining keys
704  while (i < g_opts.rows) {
705  // if rpkvar is 10, multiply rpk by number between 0.1 and 10.0
706  double a = (double)(1 + urandom(g_opts.rpkvar * g_opts.rpkvar));
707  double b = a / (double)g_opts.rpkvar;
708  double c = b * (double)g_opts.rpk;
709  const uint n = (uint)(c + 0.5);
710  // select random key to duplicate from initrows
711  const uint k = urandom(initrows);
712  uint j = 0;
713  while (i < g_opts.rows && j < n) {
714  g_keys[i].m_val.copy(g_keys[k].m_val);
715  j++;
716  i++;
717  }
718  }
719 
720  // shuffle
721  i = 0;
722  while (i < g_opts.rows) {
723  uint j = urandom(g_opts.rows);
724  if (i != j) {
725  Key tmp;
726  tmp.m_val.copy(g_keys[i].m_val);
727  g_keys[i].m_val.copy(g_keys[j].m_val);
728  g_keys[j].m_val.copy(tmp.m_val);
729  }
730  i++;
731  }
732 
733  // sort
734  sortkeys();
735 }
736 
737 // data loading
738 
739 static int
740 verifydata()
741 {
742  ll3("verifydata");
743  chkdb((g_con = g_ndb->startTransaction()) != 0);
744  chkdb((g_scan_op = g_con->getNdbScanOperation(g_tab)) != 0);
745  chkdb(g_scan_op->readTuples(NdbScanOperation::LM_CommittedRead) == 0);
746  Uint32 a;
747  Val val;
748  val.m_numattrs = g_numattrs;
749  char* a_addr = (char*)&a;
750  char* b_addr = (char*)&val.b;
751  char* c_addr = (char*)val.c;
752  char* d_addr = (char*)&val.d;
753  Uint32 no = 0;
754  NdbRecAttr* b_ra;
755  NdbRecAttr* c_ra;
756  NdbRecAttr* d_ra;
757  chkdb(g_scan_op->getValue(no++, a_addr) != 0);
758  chkdb((b_ra = g_scan_op->getValue(no++, b_addr)) != 0);
759  chkdb((c_ra = g_scan_op->getValue(no++, c_addr)) != 0);
760  chkdb((d_ra = g_scan_op->getValue(no++, d_addr)) != 0);
761  chkdb(g_con->execute(NdbTransaction::NoCommit) == 0);
762  uint count = 0;
763  uint i;
764  for (i = 0; i < g_opts.rows; i++) {
765  Key& key = g_keys[i];
766  key.m_flag = false; // not scanned
767  }
768  while (1) {
769  int ret;
770  a = ~(Uint32)0;
771  chkdb((ret = g_scan_op->nextResult()) == 0 || ret == 1);
772  if (ret == 1)
773  break;
774  val.b_null = b_ra->isNULL();
775  val.c_null = c_ra->isNULL();
776  val.d_null = d_ra->isNULL();
777  require(val.b_null == 0 || (g_b_nullable && val.b_null == 1));
778  require(val.c_null == 0 || (g_c_nullable && val.c_null == 1));
779  require(val.d_null == 0 || (g_d_nullable && val.d_null == 1));
780  i = (uint)a;
781  chkrc(i < g_opts.rows);
782  Key& key = g_keys[i];
783  chkrc(key.m_val.cmp(val) == 0);
784  chkrc(key.m_flag == false);
785  key.m_flag = true;
786  count++;
787  }
788  g_ndb->closeTransaction(g_con);
789  g_con = 0;
790  g_scan_op = 0;
791  for (i = 0; i < g_opts.rows; i++) {
792  Key& key = g_keys[i];
793  chkrc(key.m_flag == true);
794  key.m_flag = -1; // forget
795  }
796  assert(count == g_opts.rows);
797  ll3("verifydata: " << g_opts.rows << " rows");
798  return 0;
799 }
800 
801 static int
802 loaddata(bool update)
803 {
804  ll1("loaddata: update: " << update);
805  const uint batch = 512;
806  chkdb((g_con = g_ndb->startTransaction()) != 0);
807  uint i = 0;
808  while (i < g_opts.rows) {
809  chkdb((g_op = g_con->getNdbOperation(g_tab)) != 0);
810  if (!update)
811  chkdb(g_op->insertTuple() == 0);
812  else
813  chkdb(g_op->updateTuple() == 0);
814  Uint32 a = i;
815  const Val& val = g_keys[i].m_val;
816  const char* a_addr = (const char*)&a;
817  const char* b_addr = ! val.b_null ? (const char*)&val.b : 0;
818  const char* c_addr = ! val.c_null ? (const char*)val.c : 0;
819  const char* d_addr = ! val.d_null ? (const char*)&val.d : 0;
820  Uint32 no = 0;
821  chkdb(g_op->equal(no++, a_addr) == 0);
822  chkdb(g_op->setValue(no++, b_addr) == 0);
823  chkdb(g_op->setValue(no++, c_addr) == 0);
824  chkdb(g_op->setValue(no++, d_addr) == 0);
825  if (i++ % batch == 0) {
826  chkdb(g_con->execute(NdbTransaction::Commit) == 0);
827  g_ndb->closeTransaction(g_con);
828  g_con = 0;
829  g_op = 0;
830  chkdb((g_con = g_ndb->startTransaction()) != 0);
831  }
832  }
833  chkdb(g_con->execute(NdbTransaction::Commit) == 0);
834  g_ndb->closeTransaction(g_con);
835  g_con = 0;
836  g_op = 0;
837 
838  // check data and cmp routines
839  chkrc(verifydata() == 0);
840 
841  for (uint i = 0; i < g_opts.rows; i++)
842  ll3("load " << i << ": " << g_keys[i]);
843  ll0("loaddata: " << g_opts.rows << " rows");
844  return 0;
845 }
846 
847 // bounds
848 
849 struct Bnd {
850  Val m_val;
851  /*
852  * A bound is a partial key value (0 to g_numattrs attributes).
853  * It is not equal to any key value. Instead, it has a "side".
854  *
855  * side = 0 if the bound is empty
856  * side = -1 if the bound is "just before" its value
857  * side = +1 if the bound is "just after" its value
858  *
859  * This is another way of looking at strictness of non-empty
860  * start and end keys in a range.
861  *
862  * start key is strict if side = +1
863  * end key is strict if side = -1
864  *
865  * NDB API specifies strictness in the bound type of the last
866  * index attribute which is part of the start/end key.
867  *
868  * LE (0) - strict: n - side: -1
869  * LT (1) - strict: y - side: +1
870  * GE (2) - strict: n - side: +1
871  * GT (3) - strict: y - side: -1
872  *
873  * A non-empty bound divides keys into 2 disjoint subsets:
874  * keys before (cmp() == -1) and keys after (cmp() == +1).
875  */
876  int8 m_side;
877  int8 m_lohi; // 0-lo 1-hi as part of Rng
878  Bnd();
879  bool isempty() const;
880  void copy(const Bnd& bnd2); // does not copy m_lohi
881  Bnd& make(uint minattrs);
882  Bnd& make(uint minattrs, const Val& theval);
883  int cmp(const Key& key) const;
884  int cmp(const Bnd& bnd2);
885  int type(uint colno) const; // for setBound
886  void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
887 
888 private:
889  Bnd& operator=(const Bnd&);
890  Bnd(const Bnd&);
891 };
892 
893 static NdbOut&
894 operator<<(NdbOut& out, const Bnd& bnd)
895 {
896  if (bnd.m_lohi == 0)
897  out << "L";
898  else if (bnd.m_lohi == 1)
899  out << "H";
900  else
901  out << bnd.m_lohi << "?";
902  out << bnd.m_val;
903  if (bnd.m_side == 0)
904  ;
905  else if (bnd.m_side == -1)
906  out << "-";
907  else if (bnd.m_side == +1)
908  out << "+";
909  return out;
910 }
911 
912 Bnd::Bnd()
913 {
914  m_side = 0;
915  m_lohi = -1;
916 }
917 
918 bool
919 Bnd::isempty() const
920 {
921  return m_val.m_numattrs == 0;
922 }
923 
924 void
925 Bnd::copy(const Bnd& bnd2)
926 {
927  m_val.copy(bnd2.m_val);
928  m_side = bnd2.m_side;
929 }
930 
931 Bnd&
932 Bnd::make(uint minattrs)
933 {
934  require(minattrs <= g_opts.attrs);
935  require(m_lohi == 0 || m_lohi == 1);
936  uint numattrs = minattrs + urandom(g_numattrs - minattrs + 1);
937  m_val.make(numattrs, g_lim_bnd);
938  m_side = m_val.m_numattrs == 0 ? 0 : urandom(2) == 0 ? -1 : +1;
939  return *this;
940 }
941 
942 Bnd&
943 Bnd::make(uint minattrs, const Val& theval)
944 {
945  uint numattrs = minattrs + urandom(g_numattrs - minattrs);
946  m_val.copy(theval);
947  m_val.m_numattrs = numattrs;
948  m_side = m_val.m_numattrs == 0 ? 0 : urandom(2) == 0 ? -1 : +1;
949  return *this;
950 }
951 
952 int
953 Bnd::cmp(const Key& key) const
954 {
955  int place; // debug
956  int ret;
957  do {
958  int k = key.m_val.cmp(m_val, m_val.m_numattrs);
959  if (k != 0) {
960  place = 1;
961  ret = k;
962  break;
963  }
964  if (m_side != 0) {
965  place = 2;
966  ret = (-1) * m_side;
967  break;
968  }
969  place = 3;
970  ret = 0;
971  assert(m_val.m_numattrs == 0);
972  } while (0);
973  ll3("bnd: " << *this << " cmp key: " << key
974  << " ret: " << ret << " place: " << place);
975  return ret;
976 }
977 
978 int
979 Bnd::cmp(const Bnd& bnd2)
980 {
981  int place; // debug
982  int ret;
983  const Bnd& bnd1 = *this;
984  const Val& val1 = bnd1.m_val;
985  const Val& val2 = bnd2.m_val;
986  const uint numattrs1 = val1.m_numattrs;
987  const uint numattrs2 = val2.m_numattrs;
988  const uint n = (numattrs1 < numattrs2 ? numattrs1 : numattrs2);
989  do {
990  int k = val1.cmp(val2, n);
991  if (k != 0) {
992  place = 1;
993  ret = k;
994  break;
995  }
996  if (numattrs1 < numattrs2) {
997  place = 2;
998  ret = (+1) * bnd1.m_side;
999  break;
1000  }
1001  if (numattrs1 > numattrs2) {
1002  place = 3;
1003  ret = (-1) * bnd1.m_side;
1004  break;
1005  }
1006  if (bnd1.m_side < bnd2.m_side) {
1007  place = 4;
1008  ret = -1;
1009  break;
1010  }
1011  if (bnd1.m_side > bnd2.m_side) {
1012  place = 5;
1013  ret = +1;
1014  break;
1015  }
1016  place = 6;
1017  ret = 0;
1018  } while (0);
1019  ll3("bnd: " << *this << " cmp bnd: " << bnd2
1020  << " ret: " << ret << " place: " << place);
1021  return ret;
1022 }
1023 
1024 int
1025 Bnd::type(uint colno) const
1026 {
1027  int t;
1028  require(colno < m_val.m_numattrs && (m_side == -1 || m_side == +1));
1029  require(m_lohi == 0 || m_lohi == 1);
1030  if (m_lohi == 0) {
1031  if (colno + 1 < m_val.m_numattrs)
1032  t = 0; // LE
1033  else if (m_side == -1)
1034  t = 0; // LE
1035  else
1036  t = 1; // LT
1037  } else {
1038  if (colno + 1 < m_val.m_numattrs)
1039  t = 2; // GE
1040  else if (m_side == +1)
1041  t = 2; // GE
1042  else
1043  t = 3; // GT
1044  }
1045  return t;
1046 }
1047 
1048 void
1049 Bnd::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
1050 {
1051  Val& val = m_val;
1052  val.fromib(ib, j);
1053  const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
1054  const bool inclusive = (j == 0 ? ib.low_inclusive : ib.high_inclusive);
1055  if (numattrs == 0) {
1056  m_side = 0;
1057  } else {
1058  m_side = (j == 0 ? (inclusive ? -1 : +1) : (inclusive ? +1 : -1));
1059  }
1060  m_lohi = j;
1061 }
1062 
1063 // stats values
1064 
1065 struct Stval {
1066  Uint32 rir_v2;
1067  double rir;
1068  double rpk[g_numattrs];
1069  bool empty;
1070  char rule[NdbIndexStat::RuleBufferBytes];
1071  Stval();
1072 };
1073 
1074 static NdbOut&
1075 operator<<(NdbOut& out, const Stval& st)
1076 {
1077  out << "rir_v2: " << st.rir_v2;
1078  out << " rir_v4: " << st.rir;
1079  out << " rpk:[ ";
1080  for (uint k = 0; k < g_opts.attrs; k++) {
1081  if (k != 0)
1082  out << " ";
1083  out << st.rpk[k];
1084  }
1085  out << " ]";
1086  out << " " << (st.empty ? "E" : "N");
1087  out << " " << st.rule;
1088  return out;
1089 }
1090 
1091 Stval::Stval()
1092 {
1093  rir_v2 = 0;
1094  rir = 0.0;
1095  for (uint k = 0; k < g_numattrs; k++)
1096  rpk[k] = 0.0;
1097  empty = false;
1098  strcpy(rule, "-");
1099 }
1100 
1101 // ranges
1102 
1103 struct Rng {
1104  Bnd m_bnd[2];
1105  Int32 m_rowcount;
1106  // stats v2
1107  double errpct;
1108  // stats v4
1109  Stval m_st_scan; // exact stats computed from keys in range
1110  Stval m_st_stat; // interpolated kernel stats via g_is
1111  Rng();
1112  uint minattrs() const;
1113  uint maxattrs() const;
1114  bool iseq() const;
1115  bool isempty() const;
1116  void copy(const Rng& rng2);
1117  int cmp(const Key& key) const; // -1,0,+1 = key is before,in,after range
1118  uint rowcount() const;
1119  void fromib(const NdbIndexScanOperation::IndexBound& ib);
1120 
1121 private:
1122  Rng& operator=(const Rng&);
1123  Rng(const Rng&);
1124 };
1125 
1126 static NdbOut&
1127 operator<<(NdbOut& out, const Rng& rng)
1128 {
1129  out << rng.m_bnd[0] << " " << rng.m_bnd[1];
1130  if (rng.m_rowcount != -1)
1131  out << " rows: " << rng.m_rowcount;
1132  return out;
1133 }
1134 
1135 Rng::Rng()
1136 {
1137  m_bnd[0].m_lohi = 0;
1138  m_bnd[1].m_lohi = 1;
1139  m_rowcount = -1;
1140 }
1141 
1142 uint
1143 Rng::minattrs() const
1144 {
1145  return min(m_bnd[0].m_val.m_numattrs, m_bnd[1].m_val.m_numattrs);
1146 }
1147 
1148 uint
1149 Rng::maxattrs() const
1150 {
1151  return max(m_bnd[0].m_val.m_numattrs, m_bnd[1].m_val.m_numattrs);
1152 }
1153 
1154 bool
1155 Rng::iseq() const
1156 {
1157  return
1158  minattrs() == maxattrs() &&
1159  m_bnd[0].m_val.cmp(m_bnd[1].m_val, minattrs()) == 0 &&
1160  m_bnd[0].m_side < m_bnd[1].m_side;
1161 }
1162 
1163 bool
1164 Rng::isempty() const
1165 {
1166  return m_bnd[0].isempty() && m_bnd[1].isempty();
1167 }
1168 
1169 void
1170 Rng::copy(const Rng& rng2)
1171 {
1172  m_bnd[0].copy(rng2.m_bnd[0]);
1173  m_bnd[1].copy(rng2.m_bnd[1]);
1174  m_rowcount = rng2.m_rowcount;
1175 }
1176 
1177 int
1178 Rng::cmp(const Key& key) const
1179 {
1180  int place; // debug
1181  int ret;
1182  do {
1183  int k;
1184  k = m_bnd[0].cmp(key);
1185  if (k < 0) {
1186  place = 1;
1187  ret = -1;
1188  break;
1189  }
1190  k = m_bnd[1].cmp(key);
1191  if (k > 0) {
1192  place = 2;
1193  ret = +1;
1194  break;
1195  }
1196  place = 3;
1197  ret = 0;
1198  } while (0);
1199  ll3("rng: " << *this << " cmp key: " << key
1200  << " ret: " << ret << " place: " << place);
1201  return ret;
1202 }
1203 
1204 uint
1205 Rng::rowcount() const
1206 {
1207  ll3("rowcount: " << *this);
1208  int i;
1209  // binary search for first and last in range
1210  int lim[2];
1211  for (i = 0; i <= 1; i++) {
1212  ll3("search i=" << i);
1213  int lo = -1;
1214  int hi = (int)g_opts.rows;
1215  int ret;
1216  int j;
1217  do {
1218  j = (hi + lo) / 2;
1219  require(lo < j && j < hi);
1220  ret = cmp(g_keys[g_sortkeys[j]]);
1221  if (i == 0) {
1222  if (ret < 0)
1223  lo = j;
1224  else
1225  hi = j;
1226  } else {
1227  if (ret > 0)
1228  hi = j;
1229  else
1230  lo = j;
1231  }
1232  } while (hi - lo > 1);
1233  if (ret == 0)
1234  lim[i] = j;
1235  else if (i == 0)
1236  lim[i] = hi;
1237  else
1238  lim[i] = lo;
1239  }
1240 
1241  // verify is expensive due to makeranges() multiple tries
1242  const bool verify = (urandom(10) == 0);
1243  const int lo = max(lim[0], 0);
1244  const int hi = min(lim[1], (int)g_opts.rows - 1);
1245  if (verify) {
1246  int pos = -1; // before, within, after
1247  for (i = 0; i < (int)g_opts.rows; i++) {
1248  int k = cmp(g_keys[g_sortkeys[i]]);
1249  if (k < 0)
1250  require(i < lo);
1251  else if (k == 0)
1252  require(lo <= i && i <= hi);
1253  else
1254  require(i > hi);
1255  require(pos <= k);
1256  if (pos < k)
1257  pos = k;
1258  }
1259  }
1260 
1261  // result
1262  require(hi - lo + 1 >= 0);
1263  uint count = hi - lo + 1;
1264  ll3("rowcount: " << count << " lim: " << lim[0] << " " << lim[1]);
1265  return count;
1266 }
1267 
1268 void
1269 Rng::fromib(const NdbIndexScanOperation::IndexBound& ib)
1270 {
1271  for (uint j = 0; j <= 1; j++) {
1272  Bnd& bnd = m_bnd[j];
1273  bnd.fromib(ib, j);
1274  }
1275 }
1276 
1277 static Rng* g_rnglist = 0;
1278 
1279 static void
1280 freeranges()
1281 {
1282  delete [] g_rnglist;
1283  g_rnglist = 0;
1284 }
1285 
1286 static void
1287 allocranges()
1288 {
1289  freeranges();
1290  g_rnglist = new Rng [g_opts.ops];
1291  require(g_rnglist != 0);
1292 }
1293 
1294 static void
1295 makeranges()
1296 {
1297  ll1("makeranges");
1298  const uint mintries = 20;
1299  const uint maxtries = 80;
1300  const uint fudgefac = 10;
1301 
1302  for (uint i = 0; i < g_opts.ops; i++) {
1303  const bool eqpart = (urandom(100) < g_opts.eqscans);
1304  const bool eqfull = eqpart && (urandom(100) < g_opts.eqscans);
1305  Rng rng; // candidate
1306  uint j;
1307  for (j = 0; j < maxtries; j++) {
1308  Rng rng2;
1309  if (!eqpart) {
1310  rng2.m_bnd[0].make(0);
1311  rng2.m_bnd[1].make(0);
1312  } else {
1313  const uint mincnt = eqfull ? g_opts.attrs : 1;
1314  rng2.m_bnd[0].make(mincnt);
1315  rng2.m_bnd[1].copy(rng2.m_bnd[0]);
1316  rng2.m_bnd[0].m_side = -1;
1317  rng2.m_bnd[1].m_side = +1;
1318  require(rng2.iseq());
1319  }
1320  rng2.m_rowcount = (Int32)rng2.rowcount();
1321  // 0-discard 1-replace or accept 2-accept
1322  int action = 0;
1323  do {
1324  // first candidate
1325  if (rng.m_rowcount == -1) {
1326  action = 1;
1327  break;
1328  }
1329  require(rng.m_rowcount != -1);
1330  // prefer some bounds
1331  if (rng2.isempty()) {
1332  if (urandom(fudgefac) != 0)
1333  action = 0;
1334  else
1335  action = 1;
1336  break;
1337  }
1338  // prefer some rows
1339  if (rng2.m_rowcount == 0) {
1340  action = 0;
1341  break;
1342  }
1343  // accept if row count under given pct
1344  require((uint)rng2.m_rowcount <= g_opts.rows);
1345  if (100 * (uint)rng2.m_rowcount <= g_opts.scanpct * g_opts.rows) {
1346  if (urandom(fudgefac) != 0) {
1347  action = 2;
1348  break;
1349  }
1350  }
1351  // replace if less rows
1352  if (rng2.m_rowcount < rng.m_rowcount) {
1353  if (urandom(fudgefac) != 0) {
1354  action = 1;
1355  break;
1356  }
1357  }
1358  } while (0);
1359  if (action != 0) {
1360  rng.copy(rng2);
1361  if (action == 2 || j >= mintries)
1362  break;
1363  }
1364  }
1365  g_rnglist[i].copy(rng);
1366  ll2("rng " << i << ": " << rng << " tries: " << j);
1367  }
1368 }
1369 
1370 // verify ranges via range scans
1371 
1372 static int
1373 setbounds(const Rng& rng)
1374 {
1375  // currently must do each attr in order
1376  ll3("setbounds: " << rng);
1377  uint i;
1378  const Bnd (&bnd)[2] = rng.m_bnd;
1379  for (i = 0; i < g_numattrs; i++) {
1380  const Uint32 no = i; // index attribute number
1381  uint j;
1382  int type[2] = { -1, -1 };
1383  // determine inclusivity (boundtype) of upper+lower bounds on this col.
1384  // -1 == no bound on the col.
1385  for (j = 0; j <= 1; j++) {
1386  if (no < bnd[j].m_val.m_numattrs)
1387  type[j] = bnd[j].type(no);
1388  }
1389  for (j = 0; j <= 1; j++) {
1390  int t = type[j];
1391  if (t == -1)
1392  continue;
1393  if (no + 1 < bnd[j].m_val.m_numattrs)
1394  t &= ~(uint)1; // strict bit is set on last bound only
1395  const Val& val = bnd[j].m_val;
1396  const void* addr = 0;
1397  if (no == 0)
1398  addr = ! val.b_null ? (const void*)&val.b : 0;
1399  else if (no == 1)
1400  addr = ! val.c_null ? (const void*)val.c : 0;
1401  else if (no == 2)
1402  addr = ! val.d_null ? (const void*)&val.d : 0;
1403  else
1404  assert(false);
1405  ll3("setBound attr:" << no << " type:" << t << " val: " << val);
1406  chkdb(g_rangescan_op->setBound(no, t, addr) == 0);
1407  }
1408  }
1409  return 0;
1410 }
1411 
1412 static int
1413 scanrange(const Rng& rng)
1414 {
1415  ll3("scanrange: " << rng);
1416  chkdb((g_con = g_ndb->startTransaction()) != 0);
1417  chkdb((g_rangescan_op = g_con->getNdbIndexScanOperation(g_ind, g_tab)) != 0);
1418  chkdb(g_rangescan_op->readTuples() == 0);
1419  chkrc(setbounds(rng) == 0);
1420  Uint32 a;
1421  char* a_addr = (char*)&a;
1422  Uint32 no = 0;
1423  chkdb(g_rangescan_op->getValue(no++, a_addr) != 0);
1424  chkdb(g_con->execute(NdbTransaction::NoCommit) == 0);
1425  uint count = 0;
1426  uint i;
1427  for (i = 0; i < g_opts.rows; i++) {
1428  Key& key = g_keys[i];
1429  key.m_flag = false; // not scanned
1430  }
1431  while (1) {
1432  int ret;
1433  a = ~(Uint32)0;
1434  chkdb((ret = g_rangescan_op->nextResult()) == 0 || ret == 1);
1435  if (ret == 1)
1436  break;
1437  i = (uint)a;
1438  chkrc(i < g_opts.rows);
1439  Key& key = g_keys[i];
1440  ll3("scan: " << key);
1441  int k = rng.cmp(key);
1442  chkrc(k == 0);
1443  chkrc(key.m_flag == false);
1444  key.m_flag = true;
1445  count++;
1446  }
1447  g_ndb->closeTransaction(g_con);
1448  g_con = 0;
1449  g_rangescan_op = 0;
1450 
1451  for (i = 0; i < g_opts.rows; i++) {
1452  Key& key = g_keys[i];
1453  int k = rng.cmp(key);
1454  if (k != 0) // not in range
1455  chkrc(key.m_flag == false);
1456  else
1457  chkrc(key.m_flag == true);
1458  key.m_flag = -1; // forget
1459  }
1460  require((uint)rng.m_rowcount == count);
1461  return 0;
1462 }
1463 
1464 static int
1465 scanranges()
1466 {
1467  ll1("scanranges");
1468  for (uint i = 0; i < g_opts.ops; i++) {
1469  const Rng& rng = g_rnglist[i];
1470  chkrc(scanrange(rng) == 0);
1471  }
1472  return 0;
1473 }
1474 
1475 // stats v4 update
1476 
1477 static int
1478 definestat()
1479 {
1480  ll1("definestat");
1481  require(g_is != 0 && g_ind != 0 && g_tab != 0);
1482  chkdb(g_is->set_index(*g_ind, *g_tab) == 0);
1483  return 0;
1484 }
1485 
1486 static int
1487 updatestat()
1488 {
1489  ll1("updatestat");
1490  if (urandom(2) == 0) {
1491  g_dic = g_ndb->getDictionary();
1492  chkdb(g_dic->updateIndexStat(*g_ind, *g_tab) == 0);
1493  g_dic = 0;
1494  } else {
1495  chkdb(g_is->update_stat(g_ndb_sys) == 0);
1496  }
1497  return 0;
1498 }
1499 
1500 static int
1501 readstat()
1502 {
1503  ll1("readstat");
1504 
1505  NdbIndexStat::Head head;
1506  chkdb(g_is->read_head(g_ndb_sys) == 0);
1507  g_is->get_head(head);
1508  chkrc(head.m_found == true);
1509  chkrc(head.m_sampleVersion != 0);
1510  ll1("readstat:"
1511  << " sampleVersion: " << head.m_sampleVersion
1512  << " sampleCount: " << head.m_sampleCount);
1513 
1514  NdbIndexStat::CacheInfo infoQuery;
1515  chkdb(g_is->read_stat(g_ndb_sys) == 0);
1516  g_is->move_cache();
1517  g_is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
1518  ll1("readstat: cache bytes: " << infoQuery.m_totalBytes);
1519  return 0;
1520 }
1521 
1522 // test polling after updatestat
1523 
1524 static int
1525 startlistener()
1526 {
1527  ll1("startlistener");
1528  chkdb(g_is->create_listener(g_ndb_sys) == 0);
1529  chkdb(g_is->execute_listener(g_ndb_sys) == 0);
1530  return 0;
1531 }
1532 
1533 static int
1534 runlistener()
1535 {
1536  ll1("runlistener");
1537  int ret;
1538  chkdb((ret = g_is->poll_listener(g_ndb_sys, 10000)) != -1);
1539  chkrc(ret == 1);
1540  // one event is expected
1541  chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
1542  chkrc(ret == 1);
1543  chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
1544  chkrc(ret == 0);
1545  return 0;
1546 }
1547 
1548 static int
1549 stoplistener()
1550 {
1551  ll1("stoplistener");
1552  chkdb(g_is->drop_listener(g_ndb_sys) != -1);
1553  return 0;
1554 }
1555 
1556 // stats queries
1557 
1558 // exact stats from scan results
1559 static void
1560 queryscan(Rng& rng)
1561 {
1562  ll3("queryscan");
1563 
1564  uint rir;
1565  uint unq[g_numattrs];
1566  rir = 0;
1567  for (uint k = 0; k < g_opts.attrs; k++)
1568  unq[0] = 0;
1569  Key prevkey;
1570  for (uint i = 0; i < g_opts.rows; i++) {
1571  const Key& key = g_keys[g_sortkeys[i]];
1572  int res = rng.cmp(key);
1573  if (res != 0)
1574  continue;
1575  rir++;
1576  if (rir == 1) {
1577  for (uint k = 0; k < g_opts.attrs; k++)
1578  unq[k] = 1;
1579  } else {
1580  uint num_eq = ~0;
1581  int res = prevkey.m_val.cmp(key.m_val, g_opts.attrs, &num_eq);
1582  if (res == 0)
1583  require(num_eq == g_opts.attrs);
1584  else {
1585  require(res < 0);
1586  require(num_eq < g_opts.attrs);
1587  unq[num_eq]++;
1588  // propagate down
1589  for (uint k = num_eq + 1; k < g_opts.attrs; k++)
1590  unq[k]++;
1591  }
1592  }
1593  prevkey.m_val.copy(key.m_val);
1594  }
1595  require(rng.m_rowcount != -1);
1596  require((uint)rng.m_rowcount == rir);
1597 
1598  Stval& st = rng.m_st_scan;
1599  st.rir_v2 = rir;
1600  st.rir = rir == 0 ? 1.0 : (double)rir;
1601  for (uint k = 0; k < g_opts.attrs; k++) {
1602  if (rir == 0)
1603  st.rpk[k] = 1.0;
1604  else {
1605  require(rir >= unq[k]);
1606  require(unq[k] != 0);
1607  st.rpk[k] = (double)rir / (double)unq[k];
1608  }
1609  }
1610  st.empty = (rir == 0);
1611  ll2("queryscan: " << st);
1612 }
1613 
1614 /* This method initialises the passed in IndexBound
1615  * to represent the range passed in.
1616  * It assumes that the storage pointed to by low_key
1617  * and high_key in the passed IndexBound can be overwritten
1618  * and is long enough to store the data
1619  */
1620 static int
1621 initialiseIndexBound(const Rng& rng,
1623  my_record* low_key, my_record* high_key)
1624 {
1625  ll3("initialiseIndexBound: " << rng);
1626  uint i;
1627  const Bnd (&bnd)[2] = rng.m_bnd;
1628  Uint32 colsInBound[2]= {0, 0};
1629  bool boundInclusive[2]= {false, false};
1630 
1631  memset(&ib, 0xf1, sizeof(ib));
1632  memset(low_key, 0xf2, sizeof(*low_key));
1633  memset(high_key, 0xf3, sizeof(*high_key));
1634 
1635  // Clear nullbit storage
1636  low_key->m_null_bm = 0;
1637  high_key->m_null_bm = 0;
1638 
1639  for (i = 0; i < g_numattrs; i++) {
1640  const Uint32 no = i; // index attribute number
1641  uint j;
1642  int type[2] = { -1, -1 };
1643  // determine inclusivity (boundtype) of upper+lower bounds on this col.
1644  // -1 == no bound on the col.
1645  for (j = 0; j <= 1; j++) {
1646  if (no < bnd[j].m_val.m_numattrs)
1647  type[j] = bnd[j].type(no);
1648  }
1649  for (j = 0; j <= 1; j++) {
1650  /* Get ptr to key storage space for this bound */
1651  my_record* keyBuf= (j==0) ? low_key : high_key;
1652  int t = type[j];
1653  if (t == -1)
1654  continue;
1655  colsInBound[j]++;
1656 
1657  if (no + 1 >= bnd[j].m_val.m_numattrs)
1658  // Last column in bound, inclusive if GE or LE (or EQ)
1659  // i.e. bottom bit of boundtype is clear
1660  boundInclusive[j]= !(t & 1);
1661 
1662  const Val& val = bnd[j].m_val;
1663  if (no == 0)
1664  {
1665  if (! val.b_null)
1666  keyBuf->m_b= val.b;
1667 
1668  if (g_b_nullable)
1669  keyBuf->m_null_bm |= ((val.b_null?1:0) << g_ndbrec_b_nb_offset);
1670  }
1671  else if (no == 1)
1672  {
1673  if (! val.c_null)
1674  memcpy(&keyBuf->m_c[0], (const void*)&val.c, 1+ g_charlen);
1675 
1676  if (g_c_nullable)
1677  keyBuf->m_null_bm |= ((val.c_null?1:0) << g_ndbrec_c_nb_offset);
1678  }
1679  else if (no == 2)
1680  {
1681  if (! val.d_null)
1682  keyBuf->m_d= val.d;
1683 
1684  if (g_d_nullable)
1685  keyBuf->m_null_bm |= ((val.d_null?1:0) << g_ndbrec_d_nb_offset);
1686  }
1687  else
1688  require(false);
1689  ll3("initialiseIndexBound attr:" << no << " type:" << t << " val: " << val);
1690  }
1691  }
1692 
1693  /* Now have everything we need to initialise the IndexBound */
1694  ib.low_key = (char*)low_key;
1695  ib.low_key_count= colsInBound[0];
1696  ib.low_inclusive= boundInclusive[0];
1697  ib.high_key = (char*)high_key;
1698  ib.high_key_count= colsInBound[1];
1699  ib.high_inclusive= boundInclusive[1];
1700  ib.range_no= 0;
1701 
1702  ll3(" indexBound low_key_count=" << ib.low_key_count <<
1703  " low_inc=" << ib.low_inclusive <<
1704  " high_key_count=" << ib.high_key_count <<
1705  " high_inc=" << ib.high_inclusive);
1706  ll3(" low bound b=" << *((Uint32*) &ib.low_key[g_ndbrec_b_offset]) <<
1707  " d=" << *((Uint16*) &ib.low_key[g_ndbrec_d_offset]) <<
1708  " first byte=" << ib.low_key[0]);
1709  ll3(" high bound b=" << *((Uint32*) &ib.high_key[g_ndbrec_b_offset]) <<
1710  " d=" << *((Uint16*) &ib.high_key[g_ndbrec_d_offset]) <<
1711  " first byte=" << ib.high_key[0]);
1712 
1713  // verify by reverse
1714  {
1715  Rng rng;
1716  rng.fromib(ib);
1717  require(rng.m_bnd[0].cmp(bnd[0]) == 0);
1718  require(rng.m_bnd[1].cmp(bnd[1]) == 0);
1719  }
1720  return 0;
1721 }
1722 
1723 static int
1724 querystat_v2(Rng& rng)
1725 {
1726  ll3("querystat_v2");
1727 
1728  /* Create IndexBound and key storage space */
1730  my_record low_key;
1731  my_record high_key;
1732 
1733  chkdb((g_con = g_ndb->startTransaction()) != 0);
1734  chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
1735 
1736  Uint64 count = ~(Uint64)0;
1737  chkdb(g_is->records_in_range(g_ind,
1738  g_con,
1739  g_ind_rec,
1740  g_tab_rec,
1741  &ib,
1742  0,
1743  &count,
1744  0) == 0);
1745  g_ndb->closeTransaction(g_con);
1746  g_con = 0;
1747  g_rangescan_op = 0;
1748 
1749  Stval& st = rng.m_st_stat;
1750  chkrc(count < (1 << 30));
1751  st.rir_v2 = count;
1752  ll2("querystat_v2: " << st.rir_v2 << " rows");
1753  return 0;
1754 }
1755 
1756 static int
1757 querystat(Rng& rng)
1758 {
1759  ll3("querystat");
1760 
1761  // set up range
1762  Uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes];
1763  Uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes];
1764  NdbIndexStat::Bound bound_lo(g_is, bound_lo_buffer);
1765  NdbIndexStat::Bound bound_hi(g_is, bound_hi_buffer);
1766  NdbIndexStat::Range range(bound_lo, bound_hi);
1767 
1768  // convert to IndexBound (like in mysqld)
1770  my_record low_key;
1771  my_record high_key;
1772  chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
1773  chkrc(g_is->convert_range(range, g_ind_rec, &ib) == 0);
1774 
1775  // index stat query
1776  Uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
1777  NdbIndexStat::Stat stat(stat_buffer);
1778  chkdb(g_is->query_stat(range, stat) == 0);
1779 
1780  // save result
1781  Stval& st = rng.m_st_stat;
1782  g_is->get_rir(stat, &st.rir);
1783  for (uint k = 0; k < g_opts.attrs; k++) {
1784  g_is->get_rpk(stat, k, &st.rpk[k]);
1785  }
1786  g_is->get_empty(stat, &st.empty);
1787  g_is->get_rule(stat, st.rule);
1788 
1789  ll2("querystat: " << st);
1790  return 0;
1791 }
1792 
1793 static int
1794 queryranges()
1795 {
1796  ll2("queryranges");
1797  for (uint i = 0; i < g_opts.ops; i++) {
1798  Rng& rng = g_rnglist[i];
1799  ll1("rng " << i << ": " << rng);
1800  // exact stats
1801  queryscan(rng);
1802  // interpolated stats
1803  chkrc(querystat_v2(rng) == 0);
1804  chkrc(querystat(rng) == 0);
1805  const Stval& st1 = rng.m_st_scan;
1806  const Stval& st2 = rng.m_st_stat;
1807  // if rir v2 is zero then it is exact
1808  chkrc(st2.rir_v2 != 0 || st1.rir_v2 == 0);
1809  }
1810  return 0;
1811 }
1812 
1813 // general statistics methods
1814 
1815 struct Stats : public NDBT_Stats {
1816  Stats();
1817  void add(double x2);
1818  void add(const Stats& sum2);
1819 };
1820 
1821 static NdbOut&
1822 operator<<(NdbOut& out, const Stats& st)
1823 {
1824  out << "count: " << st.getCount()
1825  << " min: " << st.getMin()
1826  << " max: " << st.getMax()
1827  << " mean: " << st.getMean()
1828  << " stddev: " << st.getStddev();
1829  return out;
1830 }
1831 
1832 Stats::Stats()
1833 {
1834 }
1835 
1836 void
1837 Stats::add(double x2)
1838 {
1839  addObservation(x2);
1840 }
1841 
1842 void
1843 Stats::add(const Stats& st2)
1844 {
1845  *this += st2;
1846 }
1847 
1848 // error statistics scan vs stat
1849 
1850 struct Sterr {
1851  Stats rir_v2;
1852  Stats rir;
1853  Stats rpk[g_numattrs];
1854  Sterr();
1855  void add(const Sterr& st2);
1856 };
1857 
1858 static NdbOut&
1859 operator<<(NdbOut& out, const Sterr& st)
1860 {
1861  out << "rir_v2: " << st.rir_v2 << endl;
1862  out << "rir_v4: " << st.rir;
1863  for (uint k = 0; k < g_opts.attrs; k++) {
1864  out << endl << "rpk[" << k << "]: " << st.rpk[k];
1865  }
1866  return out;
1867 }
1868 
1869 Sterr::Sterr()
1870 {
1871 }
1872 
1873 void
1874 Sterr::add(const Sterr& st2)
1875 {
1876  rir_v2.add(st2.rir_v2);
1877  rir.add(st2.rir);
1878  for (uint k = 0; k < g_opts.attrs; k++) {
1879  rpk[k].add(st2.rpk[k]);
1880  }
1881 }
1882 
1883 static void
1884 sumrange(const Rng& rng, Sterr& st)
1885 {
1886  const Stval& st1 = rng.m_st_scan;
1887  const Stval& st2 = rng.m_st_stat;
1888 
1889  // rir_v2 error as pct of total rows
1890  {
1891  double rows = (double)g_opts.rows;
1892  double x1 = (double)st1.rir_v2;
1893  double x2 = (double)st2.rir_v2;
1894  double x3 = 100.0 * (x2 - x1) / rows;
1895  st.rir_v2.add(x3);
1896  }
1897 
1898  // rir error as pct of total rows
1899  {
1900  double rows = (double)g_opts.rows;
1901  double x1 = st1.rir;
1902  double x2 = st2.rir;
1903  double x3 = 100.0 * (x2 - x1) / rows;
1904  st.rir.add(x3);
1905  }
1906 
1907  // rpk errors as plain diff
1908  for (uint k = 0; k < g_opts.attrs; k++) {
1909  double x1 = st1.rpk[k];
1910  double x2 = st2.rpk[k];
1911  double x3 = (x2 - x1);
1912  st.rpk[k].add(x3);
1913  }
1914 }
1915 
1916 static void
1917 sumranges(Sterr& st)
1918 {
1919  for (uint i = 0; i < g_opts.ops; i++) {
1920  const Rng& rng = g_rnglist[i];
1921  sumrange(rng, st);
1922  }
1923 }
1924 
1925 // loop and final stats
1926 
1927 static Sterr g_sterr;
1928 
1929 static void
1930 loopstats()
1931 {
1932  Sterr st;
1933  sumranges(st);
1934  if (g_opts.loops != 1) {
1935  ll0("=== loop " << g_loop << " summary ===");
1936  ll0(st);
1937  }
1938  // accumulate
1939  g_sterr.add(st);
1940 }
1941 
1942 static int
1943 loopdumps()
1944 {
1945  char file[200];
1946  if (g_opts.dump == 0)
1947  return 0;
1948  {
1949  BaseString::snprintf(file, sizeof(file),
1950  "%s.key.%d", g_opts.dump, g_loop);
1951  FILE* f = 0;
1952  chker((f = fopen(file, "w")) != 0);
1953  fprintf(f, "a");
1954  for (uint k = 0; k < g_opts.attrs; k++) {
1955  if (k == 0)
1956  fprintf(f, ",b_null,b");
1957  else if (k == 1)
1958  fprintf(f, ",c_null,c");
1959  else if (k == 2)
1960  fprintf(f, ",d_null,d");
1961  else
1962  require(false);
1963  }
1964  fprintf(f, "\n");
1965  for (uint i = 0; i < g_opts.rows; i++) {
1966  const Key& key = g_keys[g_sortkeys[i]];
1967  const Val& val = key.m_val;
1968  fprintf(f, "%u", i);
1969  for (uint k = 0; k < g_opts.attrs; k++) {
1970  if (k == 0) {
1971  fprintf(f, ",%d,", val.b_null);
1972  if (!val.b_null)
1973  fprintf(f, "%u", val.b);
1974  } else if (k == 1) {
1975  fprintf(f, ",%d,", val.c_null);
1976  if (!val.c_null)
1977  fprintf(f, "%.*s", val.c[0], &val.c[1]);
1978  } else if (k == 2) {
1979  fprintf(f, ",%d,", val.d_null);
1980  if (!val.d_null)
1981  fprintf(f, "%u", val.d);
1982  } else {
1983  require(false);
1984  }
1985  }
1986  fprintf(f, "\n");
1987  }
1988  chker(fclose(f) == 0);
1989  }
1990  {
1991  BaseString::snprintf(file, sizeof(file),
1992  "%s.range.%d", g_opts.dump, g_loop);
1993  FILE* f = 0;
1994  chker((f = fopen(file, "w")) != 0);
1995  fprintf(f, "op");
1996  for (uint j = 0; j <= 1; j++) {
1997  const char* suf = (j == 0 ? "_lo" : "_hi");
1998  fprintf(f, ",attrs%s", suf);
1999  for (uint k = 0; k < g_opts.attrs; k++) {
2000  if (k == 0)
2001  fprintf(f, ",b_null%s,b%s", suf, suf);
2002  else if (k == 1)
2003  fprintf(f, ",c_null%s,c%s", suf, suf);
2004  else if (k == 2)
2005  fprintf(f, ",d_null%s,d%s", suf, suf);
2006  else
2007  require(false);
2008  }
2009  fprintf(f, ",side%s", suf);
2010  }
2011  fprintf(f, "\n");
2012  for (uint i = 0; i < g_opts.ops; i++) {
2013  const Rng& rng = g_rnglist[i];
2014  fprintf(f, "%u", i);
2015  for (uint j = 0; j <= 1; j++) {
2016  const Bnd& bnd = rng.m_bnd[j];
2017  const Val& val = bnd.m_val;
2018  fprintf(f, ",%u", val.m_numattrs);
2019  for (uint k = 0; k < g_opts.attrs; k++) {
2020  if (k >= val.m_numattrs)
2021  fprintf(f, ",,");
2022  else if (k == 0) {
2023  fprintf(f, ",%d,", val.b_null);
2024  if (!val.b_null)
2025  fprintf(f, "%u", val.b);
2026  } else if (k == 1) {
2027  fprintf(f, ",%d,", val.c_null);
2028  if (!val.c_null)
2029  fprintf(f, "%.*s", val.c[0], &val.c[1]);
2030  } else if (k == 2) {
2031  fprintf(f, ",%d,", val.d_null);
2032  if (!val.d_null)
2033  fprintf(f, "%u", val.d);
2034  } else {
2035  require(false);
2036  }
2037  }
2038  fprintf(f, ",%d", bnd.m_side);
2039  }
2040  fprintf(f, "\n");
2041  }
2042  chker(fclose(f) == 0);
2043  }
2044  {
2045  BaseString::snprintf(file, sizeof(file),
2046  "%s.stat.%d", g_opts.dump, g_loop);
2047  FILE* f = 0;
2048  chker((f = fopen(file, "w")) != 0);
2049  fprintf(f, "op");
2050  for (uint j = 0; j <= 1; j++) {
2051  const char* suf = (j == 0 ? "_scan" : "_stat");
2052  fprintf(f, ",rir_v2%s", suf);
2053  fprintf(f, ",rir%s", suf);
2054  for (uint k = 0; k < g_opts.attrs; k++) {
2055  fprintf(f, ",rpk_%u%s", k, suf);
2056  }
2057  fprintf(f, ",empty%s", suf);
2058  if (j == 1)
2059  fprintf(f, ",rule%s", suf);
2060  }
2061  fprintf(f, "\n");
2062  for (uint i = 0; i < g_opts.ops; i++) {
2063  const Rng& rng = g_rnglist[i];
2064  fprintf(f, "%u", i);
2065  for (uint j = 0; j <= 1; j++) {
2066  const Stval& st = (j == 0 ? rng.m_st_scan : rng.m_st_stat);
2067  fprintf(f, ",%u", st.rir_v2);
2068  fprintf(f, ",%.2f", st.rir);
2069  for (uint k = 0; k < g_opts.attrs; k++) {
2070  fprintf(f, ",%.2f", st.rpk[k]);
2071  }
2072  fprintf(f, ",%d", st.empty);
2073  if (j == 1)
2074  fprintf(f, ",%s", st.rule);
2075  }
2076  fprintf(f, "\n");
2077  }
2078  chker(fclose(f) == 0);
2079  }
2080  return 0;
2081 }
2082 
2083 static void
2084 finalstats()
2085 {
2086  ll0("=== summary ===");
2087  ll0(g_sterr);
2088 }
2089 
2090 static int
2091 runtest()
2092 {
2093  ll1("sizeof Val: " << sizeof(Val));
2094  ll1("sizeof Key: " << sizeof(Key));
2095  ll1("sizeof Bnd: " << sizeof(Bnd));
2096  ll1("sizeof Rng: " << sizeof(Rng));
2097 
2098  uint seed = g_opts.seed;
2099  if (seed != 1) { // not loop number
2100  if (seed == 0) { // random
2101  seed = 2 + (ushort)getpid();
2102  }
2103  ll0("random seed is " << seed);
2104  srandom(seed);
2105  } else {
2106  ll0("random seed is " << "loop number");
2107  }
2108  g_cs = get_charset_by_name(g_csname, MYF(0));
2109  if (g_cs == 0)
2110  g_cs = get_charset_by_csname(g_csname, MY_CS_PRIMARY, MYF(0));
2111  chkrc(g_cs != 0);
2112 
2113  allockeys();
2114  allocranges();
2115  chkrc(createtable() == 0);
2116  chkrc(createindex() == 0);
2117  chkrc(createNdbRecords() == 0);
2118  chkrc(definestat() == 0);
2119  chkrc(startlistener() == 0);
2120 
2121  for (g_loop = 0; g_opts.loops == 0 || g_loop < g_opts.loops; g_loop++) {
2122  ll0("=== loop " << g_loop << " ===");
2123  uint seed = g_opts.seed;
2124  if (seed == 1) { // loop number
2125  seed = g_loop;
2126  srandom(seed);
2127  }
2128  makekeys();
2129  chkrc(loaddata(g_loop != 0) == 0);
2130  makeranges();
2131  chkrc(scanranges() == 0);
2132  chkrc(updatestat() == 0);
2133  chkrc(runlistener() == 0);
2134  chkrc(readstat() == 0);
2135  chkrc(queryranges() == 0);
2136  loopstats();
2137  chkrc(loopdumps() == 0);
2138  }
2139  finalstats();
2140 
2141  chkrc(stoplistener() == 0);
2142  if (!g_opts.keeptable)
2143  chkrc(droptable() == 0);
2144  freeranges();
2145  freekeys();
2146  return 0;
2147 }
2148 
2149 static int
2150 doconnect()
2151 {
2152  g_ncc = new Ndb_cluster_connection();
2153  require(g_ncc != 0);
2154  chkdb(g_ncc->connect(30) == 0);
2155  g_ndb = new Ndb(g_ncc, "TEST_DB");
2156  require(g_ndb != 0);
2157  chkdb(g_ndb->init() == 0 && g_ndb->waitUntilReady(30) == 0);
2158  g_ndb_sys = new Ndb(g_ncc, "mysql");
2159  require(g_ndb_sys != 0);
2160  chkdb(g_ndb_sys->init() == 0 && g_ndb_sys->waitUntilReady(30) == 0);
2161  g_is = new NdbIndexStat;
2162  require(g_is != 0);
2163  return 0;
2164 }
2165 
2166 static void
2167 dodisconnect()
2168 {
2169  delete g_is;
2170  delete g_ndb_sys;
2171  delete g_ndb;
2172  delete g_ncc;
2173 }
2174 
2175 static struct my_option
2176 my_long_options[] =
2177 {
2178  NDB_STD_OPTS("testIndexStat"),
2179  { "loglevel", NDB_OPT_NOSHORT,
2180  "Logging level in this program 0-3 (default 0)",
2181  (uchar **)&g_opts.loglevel, (uchar **)&g_opts.loglevel, 0,
2182  GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2183  { "seed", NDB_OPT_NOSHORT, "Random seed (default 0=random, 1=loop number)",
2184  (uchar **)&g_opts.seed, (uchar **)&g_opts.seed, 0,
2185  GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2186  { "loops", NDB_OPT_NOSHORT, "Number of test loops (default 1, 0=forever)",
2187  (uchar **)&g_opts.loops, (uchar **)&g_opts.loops, 0,
2188  GET_INT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0 },
2189  { "rows", NDB_OPT_NOSHORT, "Number of rows (default 10000)",
2190  (uchar **)&g_opts.rows, (uchar **)&g_opts.rows, 0,
2191  GET_UINT, REQUIRED_ARG, 100000, 0, 0, 0, 0, 0 },
2192  { "ops", NDB_OPT_NOSHORT,"Number of index scans per loop (default 100)",
2193  (uchar **)&g_opts.ops, (uchar **)&g_opts.ops, 0,
2194  GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0 },
2195  { "nullkeys", NDB_OPT_NOSHORT, "Pct nulls in each key attribute (default 10)",
2196  (uchar **)&g_opts.nullkeys, (uchar **)&g_opts.nullkeys, 0,
2197  GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
2198  { "rpk", NDB_OPT_NOSHORT, "Avg records per full key (default 10)",
2199  (uchar **)&g_opts.rpk, (uchar **)&g_opts.rpk, 0,
2200  GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
2201  { "rpkvar", NDB_OPT_NOSHORT, "Vary rpk by factor (default 10, none 1)",
2202  (uchar **)&g_opts.rpkvar, (uchar **)&g_opts.rpkvar, 0,
2203  GET_UINT, REQUIRED_ARG, 10, 0, 0, 0, 0, 0 },
2204  { "scanpct", NDB_OPT_NOSHORT,
2205  "Preferred max pct of total rows per scan (default 10)",
2206  (uchar **)&g_opts.scanpct, (uchar **)&g_opts.scanpct, 0,
2207  GET_UINT, REQUIRED_ARG, 5, 0, 0, 0, 0, 0 },
2208  { "eqscans", NDB_OPT_NOSHORT,
2209  "Pct scans for partial/full equality (default 30)",
2210  (uchar **)&g_opts.eqscans, (uchar **)&g_opts.eqscans, 0,
2211  GET_UINT, REQUIRED_ARG, 50, 0, 0, 0, 0, 0 },
2212  { "keeptable", NDB_OPT_NOSHORT,
2213  "Do not drop table at exit",
2214  (uchar **)&g_opts.keeptable, (uchar **)&g_opts.keeptable, 0,
2215  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2216  { "abort", NDB_OPT_NOSHORT, "Dump core on any error",
2217  (uchar **)&g_opts.abort, (uchar **)&g_opts.abort, 0,
2218  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
2219  { "dump", NDB_OPT_NOSHORT, "Write CSV files name.* of keys,ranges,stats",
2220  (uchar **)&g_opts.dump, (uchar **)&g_opts.dump, 0,
2221  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
2222  { 0, 0, 0,
2223  0, 0, 0,
2224  GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
2225 };
2226 
2227 const char*
2228 load_default_groups[] = { "mysql_cluster", 0 };
2229 
2230 static void
2231 short_usage_sub()
2232 {
2233  ndb_short_usage_sub(NULL);
2234 }
2235 
2236 static void
2237 usage()
2238 {
2239  ndbout << my_progname << ": ordered index stats test" << endl;
2240  ndb_usage(short_usage_sub, load_default_groups, my_long_options);
2241 }
2242 
2243 static int
2244 checkoptions()
2245 {
2246  chkrc(g_opts.rows != 0);
2247  chkrc(g_opts.nullkeys <= 100);
2248  chkrc(g_opts.rpk != 0);
2249  g_opts.rpk = min(g_opts.rpk, g_opts.rows);
2250  chkrc(g_opts.rpkvar != 0);
2251  chkrc(g_opts.scanpct <= 100);
2252  chkrc(g_opts.eqscans <= 100);
2253  // set value limits
2254  g_lim_val.all_nullable = false;
2255  g_lim_bnd.all_nullable = true;
2256  g_lim_val.b_min = g_opts.rows;
2257  g_lim_val.b_max = 2 * g_opts.rows;
2258  g_lim_bnd.b_min = 90 * g_lim_val.b_min / 100;
2259  g_lim_bnd.b_max = 110 * g_lim_val.b_max / 100;
2260  g_lim_val.c_char = "bcd";
2261  g_lim_bnd.c_char = "abcde";
2262  g_lim_val.d_min = 100;
2263  g_lim_val.d_max = 200;
2264  g_lim_bnd.d_min = 0;
2265  g_lim_bnd.d_max = 300;
2266  return 0;
2267 }
2268 
2269 static
2270 int
2271 docreate_stat_tables()
2272 {
2273  if (g_is->check_systables(g_ndb_sys) == 0)
2274  return 0;
2275  ll1("check_systables: " << g_is->getNdbError());
2276 
2277  ll0("create stat tables");
2278  chkdb(g_is->create_systables(g_ndb_sys) == 0);
2279  g_has_created_stat_tables = true;
2280  return 0;
2281 }
2282 
2283 static
2284 int
2285 dodrop_stat_tables()
2286 {
2287  if (g_has_created_stat_tables == false)
2288  return 0;
2289 
2290  ll0("drop stat tables");
2291  chkdb(g_is->drop_systables(g_ndb_sys) == 0);
2292  return 0;
2293 }
2294 
2295 static int
2296 docreate_stat_events()
2297 {
2298  if (g_is->check_sysevents(g_ndb_sys) == 0)
2299  return 0;
2300  ll1("check_sysevents: " << g_is->getNdbError());
2301 
2302  ll0("create stat events");
2303  chkdb(g_is->create_sysevents(g_ndb_sys) == 0);
2304  g_has_created_stat_events = true;
2305  return 0;
2306 }
2307 
2308 static int
2309 dodrop_stat_events()
2310 {
2311  if (g_has_created_stat_events == false)
2312  return 0;
2313 
2314  ll0("drop stat events");
2315  chkdb(g_is->drop_sysevents(g_ndb_sys) == 0);
2316  return 0;
2317 }
2318 
2319 static int
2320 docreate_sys_objects()
2321 {
2322  require(g_is != 0 && g_ndb_sys != 0);
2323  chkrc(docreate_stat_tables() == 0);
2324  chkrc(docreate_stat_events() == 0);
2325  return 0;
2326 }
2327 
2328 static int
2329 dodrop_sys_objects()
2330 {
2331  require(g_is != 0 && g_ndb_sys != 0);
2332  chkrc(dodrop_stat_events() == 0);
2333  chkrc(dodrop_stat_tables() == 0);
2334  return 0;
2335 }
2336 
2337 int
2338 main(int argc, char** argv)
2339 {
2340  ndb_init();
2341  my_progname = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
2342  uint i;
2343  ndbout << my_progname;
2344  for (i = 1; i < (uint)argc; i++)
2345  ndbout << " " << argv[i];
2346  ndbout << endl;
2347  int ret;
2348  ndb_opt_set_usage_funcs(short_usage_sub, usage);
2349  ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
2350  if (ret != 0 || argc != 0) {
2351  ll0("wrong args");
2352  return NDBT_ProgramExit(NDBT_WRONGARGS);
2353  }
2354  if (checkoptions() == -1) {
2355  ll0("invalid args");
2356  return NDBT_ProgramExit(NDBT_WRONGARGS);
2357  }
2358  if (doconnect() == -1) {
2359  ll0("connect failed");
2360  return NDBT_ProgramExit(NDBT_FAILED);
2361  }
2362  if (docreate_sys_objects() == -1) {
2363  ll0("failed to check or create stat tables and events");
2364  goto failed;
2365  }
2366  if (runtest() == -1) {
2367  ll0("test failed");
2368  goto failed;
2369  }
2370  if (dodrop_sys_objects() == -1) {
2371  ll0("failed to drop created stat tables or events");
2372  goto failed;
2373  }
2374  dodisconnect();
2375  return NDBT_ProgramExit(NDBT_OK);
2376 failed:
2377  (void)dodrop_sys_objects();
2378  dodisconnect();
2379  return NDBT_ProgramExit(NDBT_FAILED);
2380 }