1 /* Copyright (c) 2005-2007 MySQL AB
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
16 #include <ndb_global.h>
19 #include <NdbIndexStat.hpp>
20 #include <NdbTest.hpp>
22 #include <ndb_version.h>
28 * 0. err pct: count: 1000 min: -99.99 max: 99.92 avg: 6.88 stddev: 27.61
30 * 0. baseline with same options as handler
35 #define min(a, b) ((a) <= (b) ? (a) : (b))
36 #define max(a, b) ((a) >= (b) ? (a) : (b))
39 NdbOut::operator<<(double x
)
42 sprintf(buf
, "%.2f", x
);
85 const char* g_progname
= "testIndexStat";
86 static uint g_loop
= 0;
88 static const char* g_tabname
= "ts0";
89 static const char* g_indname
= "ts0x1";
90 static const char g_numattrs
= 3;
91 static const uint g_charlen
= 10;
92 static const char* g_csname
= "latin1_swedish_ci";
93 static CHARSET_INFO
* g_cs
;
95 // value and bound ranges
96 static uint g_val_b_max
= 10;
97 static uint g_bnd_b_max
= 20;
98 static const char* g_val_c_char
= "bcd";
99 static const char* g_bnd_c_char
= "abcde";
100 static uint g_val_d_max
= 100;
101 static uint g_bnd_d_max
= 200;
103 static Ndb_cluster_connection
* g_ncc
= 0;
104 static Ndb
* g_ndb
= 0;
105 static NdbDictionary::Dictionary
* g_dic
= 0;
106 static const NdbDictionary::Table
* g_tab
= 0;
107 static const NdbDictionary::Index
* g_ind
= 0;
109 static NdbIndexStat
* g_stat
= 0;
111 static NdbTransaction
* g_con
= 0;
112 static NdbOperation
* g_op
= 0;
113 static NdbScanOperation
* g_scan_op
= 0;
114 static NdbIndexScanOperation
* g_rangescan_op
= 0;
119 uint r
= (uint
)random();
133 static int& g_loglevel
= g_opts
.loglevel
; // default log level
136 do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; errdb(); if (g_opts.abort) abort(); return -1; } while (0)
139 do { if (likely(x)) break; ndbout << "line " << __LINE__ << " FAIL " << #x << endl; if (g_opts.abort) abort(); return -1; } while (0)
142 do { if (likely(x)) break; ndbout << "line " << __LINE__ << " ASSERT " << #x << endl; abort(); } while (0)
145 do { if (likely(g_loglevel < n)) break; ndbout << x << endl; } while (0)
147 #define ll0(x) llx(0, x)
148 #define ll1(x) llx(1, x)
149 #define ll2(x) llx(2, x)
150 #define ll3(x) llx(3, x)
156 // g_ncc return no error...
158 const NdbError
& e
= g_ndb
->getNdbError();
160 ll0(++any
<< " ndb: error " << e
);
163 const NdbError
& e
= g_dic
->getNdbError();
165 ll0(++any
<< " dic: error " << e
);
168 const NdbError
& e
= g_con
->getNdbError();
170 ll0(++any
<< " con: error " << e
);
173 const NdbError
& e
= g_op
->getNdbError();
175 ll0(++any
<< " op: error " << e
);
177 if (g_scan_op
!= 0) {
178 const NdbError
& e
= g_scan_op
->getNdbError();
180 ll0(++any
<< " scan_op: error " << e
);
182 if (g_rangescan_op
!= 0) {
183 const NdbError
& e
= g_rangescan_op
->getNdbError();
185 ll0(++any
<< " rangescan_op: error " << e
);
188 const NdbError
& e
= g_stat
->getNdbError();
190 ll0(++any
<< " stat: error " << e
);
193 ll0("unknown db error");
196 // create table ts0 (
197 // a int unsigned, b smallint not null, c varchar(10), d int unsigned,
198 // primary key using hash (a), index (b, c, d) )
203 NdbDictionary::Table
tab(g_tabname
);
204 tab
.setLogging(false);
206 NdbDictionary::Column
col("a");
207 col
.setType(NdbDictionary::Column::Unsigned
);
208 col
.setPrimaryKey(true);
212 NdbDictionary::Column
col("b");
213 col
.setType(NdbDictionary::Column::Smallint
);
214 col
.setNullable(false);
218 NdbDictionary::Column
col("c");
219 col
.setType(NdbDictionary::Column::Varchar
);
220 col
.setLength(g_charlen
);
221 col
.setCharset(g_cs
);
222 col
.setNullable(true);
226 NdbDictionary::Column
col("d");
227 col
.setType(NdbDictionary::Column::Unsigned
);
228 col
.setNullable(true);
231 NdbDictionary::Index
ind(g_indname
);
232 ind
.setTable(g_tabname
);
233 ind
.setType(NdbDictionary::Index::OrderedIndex
);
234 ind
.setLogging(false);
235 ind
.addColumnName("b");
236 ind
.addColumnName("c");
237 ind
.addColumnName("d");
238 g_dic
= g_ndb
->getDictionary();
239 if (! g_opts
.keeptable
) {
240 if (g_dic
->getTable(g_tabname
) != 0)
241 chkdb(g_dic
->dropTable(g_tabname
) == 0);
242 chkdb(g_dic
->createTable(tab
) == 0);
243 chkdb(g_dic
->createIndex(ind
) == 0);
245 if (g_dic
->getTable(g_tabname
) == 0) {
246 chkdb(g_dic
->createTable(tab
) == 0);
247 chkdb(g_dic
->createIndex(ind
) == 0);
249 g_opts
.loaddata
= false;
251 chkdb((g_tab
= g_dic
->getTable(g_tabname
)) != 0);
252 chkdb((g_ind
= g_dic
->getIndex(g_indname
, g_tabname
)) != 0);
260 g_dic
= g_ndb
->getDictionary();
261 if (! g_opts
.keeptable
)
262 chkdb(g_dic
->dropTable(g_tabname
) == 0);
270 uchar c
[1 + g_charlen
];
273 // partial values for use in Bnd
275 void make(uint n
= g_numattrs
, bool is_val
= true);
276 int cmp(const Val
& val
, uint n
= g_numattrs
) const;
280 operator<<(NdbOut
& out
, const Val
& val
)
283 if (val
.numattrs
>= 1) {
286 if (val
.numattrs
>= 2) {
291 char buf
[1 + g_charlen
];
292 sprintf(buf
, "%.*s", val
.c
[0], &val
.c
[1]);
293 out
<< "'" << buf
<< "'";
296 if (val
.numattrs
>= 3) {
308 Val::make(uint n
, bool is_val
)
311 uint b_max
= is_val
? g_val_b_max
: g_bnd_b_max
;
312 b
= (int)urandom(2 * b_max
) - (int)b_max
;
315 if (urandom(100) < g_opts
.nullkeys
)
318 const char* c_char
= is_val
? g_val_c_char
: g_bnd_c_char
;
320 uint len
= urandom(urandom(g_charlen
+ 2));
323 for (j
= 0; j
< len
; j
++) {
324 uint k
= urandom(strlen(c_char
));
325 c
[1 + j
] = c_char
[k
];
331 if (urandom(100) < g_opts
.nullkeys
)
334 uint d_max
= is_val
? g_val_d_max
: g_bnd_d_max
;
343 Val::cmp(const Val
& val
, uint n
) const
346 if (k
== 0 && n
>= 1) {
352 if (k
== 0 && n
>= 2) {
353 if (! c_null
&& ! val
.c_null
) {
354 const uchar
* s1
= &c
[1];
355 const uchar
* s2
= &val
.c
[1];
356 const uint l1
= (uint
)c
[0];
357 const uint l2
= (uint
)val
.c
[0];
358 assert(l1
<= g_charlen
&& l2
<= g_charlen
);
359 k
= g_cs
->coll
->strnncollsp(g_cs
, s1
, l1
, s2
, l2
, 0);
360 } else if (! c_null
) {
362 } else if (! val
.c_null
) {
366 if (k
== 0 && n
>= 3) {
367 if (! d_null
&& ! val
.d_null
) {
372 } else if (! d_null
) {
374 } else if (! val
.d_null
) {
391 operator<<(NdbOut
& out
, const Key
& key
)
393 out
<< key
.val
<< " info:" << key
.count
;
397 static Key
* g_keys
= 0;
398 static Key
* g_sortkeys
= 0;
399 static uint g_sortcount
= 0;
400 static Key
* g_minkey
= 0;
401 static Key
* g_maxkey
= 0;
407 my_free((char*)g_keys
, MYF(0));
409 my_free((char*)g_sortkeys
, MYF(0));
418 size_t sz
= sizeof(Key
) * g_opts
.rows
;
419 g_keys
= (Key
*)my_malloc(sz
, MYF(0));
420 g_sortkeys
= (Key
*)my_malloc(sz
, MYF(0));
421 chkrc(g_keys
!= 0 && g_sortkeys
!= 0);
422 memset(g_keys
, 0x1f, sz
);
423 memset(g_sortkeys
, 0x1f, sz
);
431 for (i
= 0; i
< g_opts
.rows
; i
++) {
432 Key
& key
= g_keys
[i
];
434 key
.flag
= false; // mark for dup generation done
436 for (i
= 0; i
< g_opts
.rows
; i
++) {
437 Key
& key
= g_keys
[i
];
442 uint n
= (urandom(fudge
* (g_opts
.dupkeys
- 100)) + 99) / 100;
444 for (k
= 1; k
< n
; k
++) {
445 uint j
= urandom(g_opts
.rows
);
447 Key
& dst
= g_keys
[j
];
453 } while (urandom(g_opts
.tryhard
) != 0);
461 const uint batch
= 512;
462 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
464 while (i
< g_opts
.rows
) {
465 chkdb((g_op
= g_con
->getNdbOperation(g_tab
)) != 0);
466 chkdb(g_op
->insertTuple() == 0);
468 const Val
& val
= g_keys
[i
].val
;
469 const char* a_addr
= (const char*)&a
;
470 const char* b_addr
= (const char*)&val
.b
;
471 const char* c_addr
= ! val
.c_null
? (const char*)val
.c
: 0;
472 const char* d_addr
= ! val
.d_null
? (const char*)&val
.d
: 0;
474 chkdb(g_op
->equal(no
++, a_addr
) == 0);
475 chkdb(g_op
->setValue(no
++, b_addr
) == 0);
476 chkdb(g_op
->setValue(no
++, c_addr
) == 0);
477 chkdb(g_op
->setValue(no
++, d_addr
) == 0);
478 if (i
++ % batch
== 0) {
479 chkdb(g_con
->execute(NdbTransaction::Commit
) == 0);
480 g_ndb
->closeTransaction(g_con
);
483 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
486 chkdb(g_con
->execute(NdbTransaction::Commit
) == 0);
487 g_ndb
->closeTransaction(g_con
);
490 ll0(g_tabname
<< ": inserted " << g_opts
.rows
<< " rows");
499 char* r_addr
= (char*)&r
;
500 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
501 chkdb((g_scan_op
= g_con
->getNdbScanOperation(g_tab
)) != 0);
502 chkdb(g_scan_op
->readTuples() == 0);
503 chkdb(g_scan_op
->interpret_exit_last_row() == 0);
504 chkdb(g_scan_op
->getValue(NdbDictionary::Column::ROW_COUNT
, r_addr
) != 0);
505 chkdb(g_con
->execute(NdbTransaction::NoCommit
) == 0);
509 chkdb((ret
= g_scan_op
->nextResult()) == 0 || ret
== 1);
514 g_ndb
->closeTransaction(g_con
);
524 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
525 chkdb((g_scan_op
= g_con
->getNdbScanOperation(g_tab
)) != 0);
526 chkdb(g_scan_op
->readTuples() == 0);
529 char* a_addr
= (char*)&a
;
530 char* b_addr
= (char*)&val
.b
;
531 char* c_addr
= (char*)val
.c
;
532 char* d_addr
= (char*)&val
.d
;
537 chkdb(g_scan_op
->getValue(no
++, a_addr
) != 0);
538 chkdb((b_ra
= g_scan_op
->getValue(no
++, b_addr
)) != 0);
539 chkdb((c_ra
= g_scan_op
->getValue(no
++, c_addr
)) != 0);
540 chkdb((d_ra
= g_scan_op
->getValue(no
++, d_addr
)) != 0);
541 chkdb(g_con
->execute(NdbTransaction::NoCommit
) == 0);
544 for (i
= 0; i
< g_opts
.rows
; i
++)
549 chkdb((ret
= g_scan_op
->nextResult()) == 0 || ret
== 1);
552 assert(b_ra
->isNULL() == 0 && c_ra
->isNULL() != -1 && d_ra
->isNULL() != -1);
553 val
.c_null
= c_ra
->isNULL();
554 val
.d_null
= d_ra
->isNULL();
556 chkrc(i
< g_opts
.rows
);
557 Key
& key
= g_keys
[i
];
559 chkrc(key
.val
.cmp(val
) == 0);
565 g_ndb
->closeTransaction(g_con
);
568 for (i
= 0; i
< g_opts
.rows
; i
++)
569 chkrc(g_keys
[i
].count
== 1);
570 assert(count
== g_opts
.rows
);
571 int level
= g_opts
.loaddata
? 1 : 0;
572 llx(level
, g_tabname
<< ": scanned " << g_opts
.rows
<< " rows");
579 if (g_opts
.loaddata
) {
580 chkrc(allockeys() == 0);
582 chkrc(insertdata() == 0);
584 chkrc(countrows() == 0);
585 chkrc(g_opts
.rows
!= 0);
586 ll0(g_tabname
<< ": using old table of " << g_opts
.rows
<< " rows");
587 chkrc(allockeys() == 0);
589 chkrc(scandata() == 0);
591 for (i
= 0; i
< g_opts
.rows
; i
++)
592 ll3(i
<< ": " << g_keys
[i
]);
596 // true = match, index = match or next higher
598 sortval(const Val
& val
, int& index
)
600 if (unlikely(g_sortcount
== 0)) {
605 int hi
= (int)g_sortcount
;
610 ret
= val
.cmp(g_sortkeys
[j
].val
);
617 } while (hi
- lo
> 1);
629 // insert sort with binary search
632 for (i
= 0; i
< g_opts
.rows
; i
++) {
633 const Val
& val
= g_keys
[i
].val
;
635 bool match
= sortval(val
, index
);
636 Key
& dst
= g_sortkeys
[index
];
640 uint bytes
= ((int)g_sortcount
- index
) * sizeof(Key
);
641 memmove(&dst
+ 1, &dst
, bytes
);
647 g_minkey
= &g_sortkeys
[0];
648 g_maxkey
= &g_sortkeys
[g_sortcount
- 1];
649 ll1("counted " << g_sortcount
<< " distinct keys");
655 * A bound is a partial key value (0 to g_numattrs attributes).
656 * It is not equal to any key value. Instead, it has a "side".
658 * side = 0 if the bound is empty
659 * side = -1 if the bound is "just before" its value
660 * side = +1 if the bound is "just after" its value
662 * This is another way of looking at strictness of non-empty
663 * start and end keys in a range.
665 * start key is strict if side = +1
666 * end key is strict if side = -1
668 * NDB API specifies strictness in the bound type of the last
669 * index attribute which is part of the start/end key.
671 * LE (0) - strict: n - side: -1
672 * LT (1) - strict: y - side: +1
673 * GE (2) - strict: n - side: +1
674 * GT (3) - strict: y - side: -1
676 * A non-empty bound divides keys into 2 disjoint subsets:
677 * keys before (cmp() == -1) and keys after (cmp() == +1).
680 Bnd
& make(uint minattrs
);
681 Bnd
& make(uint minattrs
, const Val
& theval
);
682 int cmp(const Val
& val
) const;
683 int type(uint lohi
, uint colno
) const; // for setBound
687 operator<<(NdbOut
& out
, const Bnd
& bnd
)
690 out
<< " side: " << bnd
.side
;
695 Bnd::make(uint minattrs
)
697 uint numattrs
= minattrs
+ urandom(g_numattrs
- minattrs
);
698 val
.make(numattrs
, false);
699 side
= val
.numattrs
== 0 ? 0 : urandom(2) == 0 ? -1 : +1;
704 Bnd::make(uint minattrs
, const Val
& theval
)
706 uint numattrs
= minattrs
+ urandom(g_numattrs
- minattrs
);
708 val
.numattrs
= numattrs
;
709 side
= val
.numattrs
== 0 ? 0 : urandom(2) == 0 ? -1 : +1;
714 Bnd::cmp(const Val
& theval
) const
719 assert(theval
.numattrs
== g_numattrs
);
720 int k
= theval
.cmp(val
, val
.numattrs
);
733 assert(val
.numattrs
== 0);
735 ll3("cmp: val: " << theval
<< " bnd: " << *this <<
736 " return: " << ret
<< " at " << place
);
741 Bnd::type(uint lohi
, uint colno
) const
744 assert(lohi
<= 1 && colno
< val
.numattrs
&& (side
== -1 || side
== +1));
746 if (colno
+ 1 < val
.numattrs
)
753 if (colno
+ 1 < val
.numattrs
)
765 uint
minattrs() const;
766 uint
maxattrs() const;
767 int cmp(const Val
& val
) const; // -1,0,+1 = key is before,in,after range
768 uint
rowcount() const;
778 operator<<(NdbOut
& out
, const Range
& range
)
780 out
<< "bnd0: " << range
.bnd
[0] << " bnd1: " << range
.bnd
[1];
785 Range::minattrs() const
787 return min(bnd
[0].val
.numattrs
, bnd
[1].val
.numattrs
);
791 Range::maxattrs() const
793 return max(bnd
[0].val
.numattrs
, bnd
[1].val
.numattrs
);
797 Range::cmp(const Val
& theval
) const
803 k
= bnd
[0].cmp(theval
);
809 k
= bnd
[1].cmp(theval
);
818 ll3("cmp: val: " << theval
<< " range: " << *this <<
819 " return: " << ret
<< " at " << place
);
824 Range::rowcount() const
826 ll2("rowcount: " << *this);
828 // binary search for first and last in range
830 for (i
= 0; i
<= 1; i
++) {
831 ll3("search i=" << i
);
833 int hi
= (int)g_sortcount
;
838 ret
= cmp(g_sortkeys
[j
].val
);
850 } while (hi
- lo
> 1);
859 const int lo
= max(lim
[0], 0);
860 const int hi
= min(lim
[1], (int)g_sortcount
- 1);
861 if (! g_opts
.nochecks
) {
863 for (i
= 0; i
< (int)g_sortcount
; i
++) {
864 int k
= cmp(g_sortkeys
[i
].val
);
868 assert(lo
<= i
&& i
<= hi
);
878 for (i
= lo
; i
<= hi
; i
++)
879 count
+= g_sortkeys
[i
].count
;
880 ll2("count: " << count
<< " index lim: " << lim
[0] << " " << lim
[1]);
888 minattrs() == maxattrs() &&
889 bnd
[0].val
.cmp(bnd
[1].val
, minattrs()) == 0 &&
890 bnd
[0].side
< bnd
[1].side
;
893 static Range
* g_ranges
= 0;
899 my_free((char*)g_ranges
, MYF(0));
907 size_t sz
= sizeof(Range
) * g_opts
.ops
;
908 g_ranges
= (Range
*)my_malloc(sz
, MYF(0));
909 chkrc(g_ranges
!= 0);
910 memset(g_ranges
, 0x1f, sz
);
918 for (i
= 0; i
< g_opts
.ops
; i
++) {
919 Range
& range
= g_ranges
[i
];
920 range
.flag
= false; // mark for dup generation done
921 bool fulleq
= (urandom(100) < g_opts
.eqscans
);
922 bool eq
= fulleq
|| (urandom(100) < g_opts
.eqscans
);
923 bool matcheq
= eq
&& (urandom(10) != 0);
925 // random but prefer non-empty and no more than scanpct
927 range
.bnd
[0].make(0);
928 range
.bnd
[1].make(0);
929 uint count
= range
.rowcount();
930 if (count
!= 0 && 100 * count
<= g_opts
.scanpct
* g_opts
.rows
)
932 } while (urandom(g_opts
.tryhard
) != 0);
934 uint minattrs
= fulleq
? g_numattrs
: 1;
936 range
.bnd
[0].make(minattrs
);
938 uint m
= urandom(g_sortcount
);
939 const Val
& val
= g_sortkeys
[m
].val
;
940 range
.bnd
[0].make(minattrs
, val
);
942 range
.bnd
[1] = range
.bnd
[0];
943 range
.bnd
[0].side
= -1;
944 range
.bnd
[1].side
= +1;
948 assert(range
.iseq());
951 for (i
= 0; i
< g_opts
.ops
; i
++) {
952 Range
& range
= g_ranges
[i
];
956 if (urandom(100) < g_opts
.dupscans
) {
957 uint j
= urandom(g_opts
.ops
);
959 Range
& dst
= g_ranges
[j
];
961 dst
.bnd
[0] = range
.bnd
[0];
962 dst
.bnd
[1] = range
.bnd
[1];
966 } while (urandom(g_opts
.tryhard
) != 0);
972 setbounds(const Range
& range
)
974 // currently must do each attr in order
975 ll2("setbounds: " << range
);
977 const Bnd (&bnd
)[2] = range
.bnd
;
978 for (i
= 0; i
< g_numattrs
; i
++) {
979 const Uint32 no
= i
; // index attribute number
981 int type
[2] = { -1, -1 };
982 for (j
= 0; j
<= 1; j
++) {
983 if (no
< bnd
[j
].val
.numattrs
)
984 type
[j
] = bnd
[j
].type(j
, no
);
986 for (j
= 0; j
<= 1; j
++) {
990 if (no
+ 1 < bnd
[j
].val
.numattrs
)
991 t
&= ~(uint
)1; // strict bit is set on last bound only
992 const Val
& val
= bnd
[j
].val
;
993 const void* addr
= 0;
995 addr
= (const void*)&val
.b
;
997 addr
= ! val
.c_null
? (const void*)val
.c
: 0;
999 addr
= ! val
.d_null
? (const void*)&val
.d
: 0;
1002 ll2("setBound attr:" << no
<< " type:" << t
<< " val: " << val
);
1003 chkdb(g_rangescan_op
->setBound(no
, t
, addr
) == 0);
1012 g_stat
= new NdbIndexStat(g_ind
);
1013 chkdb(g_stat
->alloc_cache(32) == 0);
1018 runstat(Range
& range
, int flags
)
1020 ll2("runstat: " << range
<< " flags=" << flags
);
1021 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
1022 chkdb((g_rangescan_op
= g_con
->getNdbIndexScanOperation(g_ind
, g_tab
)) != 0);
1023 chkdb(g_rangescan_op
->readTuples(NdbOperation::LM_CommittedRead
) == 0);
1024 chkrc(setbounds(range
) == 0);
1025 Uint64 count
= ~(Uint64
)0;
1026 chkdb(g_stat
->records_in_range(g_ind
, g_rangescan_op
, g_opts
.rows
, &count
, flags
) == 0);
1027 g_ndb
->closeTransaction(g_con
);
1030 range
.statrows
= (uint
)count
;
1031 chkrc((Uint64
)range
.statrows
== count
);
1032 ll2("stat: " << range
.statrows
);
1037 runscan(Range
& range
)
1039 ll2("runscan: " << range
);
1040 chkdb((g_con
= g_ndb
->startTransaction()) != 0);
1041 chkdb((g_rangescan_op
= g_con
->getNdbIndexScanOperation(g_ind
, g_tab
)) != 0);
1042 chkdb(g_rangescan_op
->readTuples() == 0);
1043 chkrc(setbounds(range
) == 0);
1045 char* a_addr
= (char*)&a
;
1047 chkdb(g_rangescan_op
->getValue(no
++, a_addr
) != 0);
1048 chkdb(g_con
->execute(NdbTransaction::NoCommit
) == 0);
1051 for (i
= 0; i
< g_opts
.rows
; i
++)
1052 g_keys
[i
].count
= 0;
1056 chkdb((ret
= g_rangescan_op
->nextResult()) == 0 || ret
== 1);
1060 chkrc(i
< g_opts
.rows
);
1061 Key
& key
= g_keys
[i
];
1062 ll3("scan: " << key
);
1063 int k
= range
.cmp(key
.val
);
1065 chkrc(key
.count
== 0);
1069 g_ndb
->closeTransaction(g_con
);
1072 if (! g_opts
.nochecks
) {
1073 for (i
= 0; i
< g_opts
.rows
; i
++) {
1074 const Key
& key
= g_keys
[i
];
1075 int k
= range
.cmp(key
.val
);
1076 assert((k
!= 0 && key
.count
== 0) || (k
== 0 && key
.count
== 1));
1078 assert(range
.rowcount() == count
);
1080 range
.scanrows
= count
;
1081 ll2("scan: " << range
.scanrows
);
1089 for (i
= 0; i
< g_opts
.ops
; i
++) {
1090 Range
& range
= g_ranges
[i
];
1091 ll1("range " << i
<< ": " << range
);
1092 // simulate old handler code
1094 if (i
< 32 || i
% 20 == 0)
1095 flags
|= NdbIndexStat::RR_UseDb
;
1096 chkrc(runstat(range
, flags
) == 0);
1097 chkrc(runscan(range
) == 0);
1098 // if stat is 0 then it is exact scan count
1099 chkrc(range
.statrows
!= 0 || range
.scanrows
== 0);
1100 // measure error as fraction of total rows
1101 double x
= (double)range
.statrows
;
1102 double y
= (double)range
.scanrows
;
1103 double z
= (double)g_opts
.rows
;
1104 double err
= (x
- y
) / z
;
1106 range
.errpct
= 100.0 * err
;
1107 ll1("range " << i
<< ":" <<
1108 " stat: " << range
.statrows
<< " scan: " << range
.scanrows
<<
1109 " errpct: " << range
.errpct
);
1125 void add(const Stat
& stat
);
1133 sum
= minval
= maxval
= avg
= varsum
= var
= stddev
= 0.0;
1137 Stat::add(const Stat
& stat
)
1144 tmp
.count
= count
+ stat
.count
;
1145 tmp
.sum
= sum
+ stat
.sum
;
1146 tmp
.minval
= minval
<= stat
.minval
? minval
: stat
.minval
;
1147 tmp
.maxval
= maxval
>= stat
.maxval
? maxval
: stat
.maxval
;
1148 tmp
.avg
= tmp
.sum
/ double(tmp
.count
);
1149 tmp
.varsum
= varsum
+ stat
.varsum
;
1150 tmp
.var
= tmp
.varsum
/ double(tmp
.count
);
1151 tmp
.stddev
= sqrt(tmp
.var
);
1156 operator<<(NdbOut
& out
, const Stat
& stat
)
1158 out
<< stat
.name
<< ": " << "count: " << stat
.count
1159 << " min: " << stat
.minval
<< " max: " << stat
.maxval
1160 << " avg: " << stat
.avg
<< " stddev: " << stat
.stddev
;
1164 template <class T
, class V
>
1166 computestat(Stat
& stat
)
1169 stat
.name
= V::name();
1170 const T
* array
= V::array();
1171 stat
.count
= V::count();
1172 assert(stat
.count
!= 0);
1174 for (i
= 0; i
< stat
.count
; i
++) {
1175 const T
& item
= array
[i
];
1176 double data
= V::data(item
);
1179 stat
.minval
= stat
.maxval
= data
;
1181 if (stat
.minval
> data
)
1183 if (stat
.maxval
< data
)
1187 stat
.avg
= stat
.sum
/ double(stat
.count
);
1189 for (i
= 0; i
< stat
.count
; i
++) {
1190 const T
& item
= array
[i
];
1191 double data
= V::data(item
);
1192 double x
= data
- stat
.avg
;
1193 stat
.varsum
+= x
* x
;
1195 stat
.var
= stat
.varsum
/ double(stat
.count
);
1196 stat
.stddev
= sqrt(stat
.var
);
1200 static const char* name() { return "rec per key"; }
1201 static const Key
* array() { return g_sortkeys
; }
1202 static uint
count() { return g_sortcount
; }
1203 static double data(const Key
& key
) { return (double)key
.rpk
; }
1207 static const char* name() { return "rir err pct"; }
1208 static const Range
* array() { return g_ranges
; }
1209 static uint
count() { return g_opts
.ops
; }
1210 static double data(const Range
& range
) { return (double)range
.errpct
; }
1213 template void computestat
<Key
, V_rpk
>(Stat
& stat
);
1214 template void computestat
<Range
, V_rir
>(Stat
& stat
);
1216 static Stat g_stat_rpk
; // summaries over loops
1217 static Stat g_stat_rir
;
1222 Stat stat_rpk
; // records per key
1223 Stat stat_rir
; // record in range
1228 computestat
<Key
, V_rpk
>(stat_rpk
);
1229 computestat
<Range
, V_rir
>(stat_rir
);
1230 if (g_opts
.loop
!= 1) {
1231 ll0("=== loop " << g_loop
<< " summary ===");
1236 g_stat_rpk
.add(stat_rpk
);
1237 g_stat_rir
.add(stat_rir
);
1243 ll0("=== summary ===");
1253 if (g_opts
.seed
== 0)
1255 if (g_opts
.seed
!= -1)
1256 seed
= (uint
)g_opts
.seed
;
1258 seed
= 1 + (ushort
)getpid();
1260 if (g_opts
.seed
!= 0)
1264 ll0("seed=" << seed
);
1272 g_cs
= get_charset_by_name(g_csname
, MYF(0));
1274 g_cs
= get_charset_by_csname(g_csname
, MY_CS_PRIMARY
, MYF(0));
1276 for (g_loop
= 0; g_opts
.loop
== 0 || g_loop
< g_opts
.loop
; g_loop
++) {
1277 ll0("=== loop " << g_loop
<< " ===");
1279 chkrc(createtable() == 0);
1280 chkrc(loaddata() == 0);
1282 chkrc(allocranges() == 0);
1284 chkrc(allocstat() == 0);
1285 chkrc(runscans() == 0);
1286 chkrc(droptable() == 0);
1295 static struct my_option
1298 NDB_STD_OPTS("testIndexStat"),
1299 { "loglevel", 1001, "Logging level in this program 0-3 (default 0)",
1300 (uchar
**)&g_opts
.loglevel
, (uchar
**)&g_opts
.loglevel
, 0,
1301 GET_INT
, REQUIRED_ARG
, 0, 0, 0, 0, 0, 0 },
1302 { "seed", 1002, "Random seed (0=loop number, default -1=random)",
1303 (uchar
**)&g_opts
.seed
, (uchar
**)&g_opts
.seed
, 0,
1304 GET_INT
, REQUIRED_ARG
, -1, 0, 0, 0, 0, 0 },
1305 { "loop", 1003, "Number of test loops (default 1, 0=forever)",
1306 (uchar
**)&g_opts
.loop
, (uchar
**)&g_opts
.loop
, 0,
1307 GET_INT
, REQUIRED_ARG
, 1, 0, 0, 0, 0, 0 },
1308 { "rows", 1004, "Number of rows (default 100000)",
1309 (uchar
**)&g_opts
.rows
, (uchar
**)&g_opts
.rows
, 0,
1310 GET_UINT
, REQUIRED_ARG
, 100000, 0, 0, 0, 0, 0 },
1311 { "ops", 1005, "Number of index scans per loop (default 1000)",
1312 (uchar
**)&g_opts
.ops
, (uchar
**)&g_opts
.ops
, 0,
1313 GET_UINT
, REQUIRED_ARG
, 1000, 0, 0, 0, 0, 0 },
1314 { "dupkeys", 1006, "Pct records per key (min 100, default 1000)",
1315 (uchar
**)&g_opts
.dupkeys
, (uchar
**)&g_opts
.dupkeys
, 0,
1316 GET_UINT
, REQUIRED_ARG
, 1000, 0, 0, 0, 0, 0 },
1317 { "scanpct", 1007, "Preferred max pct of total rows per scan (default 5)",
1318 (uchar
**)&g_opts
.scanpct
, (uchar
**)&g_opts
.scanpct
, 0,
1319 GET_UINT
, REQUIRED_ARG
, 5, 0, 0, 0, 0, 0 },
1320 { "nullkeys", 1008, "Pct nulls in each key attribute (default 10)",
1321 (uchar
**)&g_opts
.nullkeys
, (uchar
**)&g_opts
.nullkeys
, 0,
1322 GET_UINT
, REQUIRED_ARG
, 10, 0, 0, 0, 0, 0 },
1323 { "eqscans", 1009, "Pct scans for partial/full equality (default 50)",
1324 (uchar
**)&g_opts
.eqscans
, (uchar
**)&g_opts
.eqscans
, 0,
1325 GET_UINT
, REQUIRED_ARG
, 50, 0, 0, 0, 0, 0 },
1326 { "dupscans", 1010, "Pct scans using same bounds (default 10)",
1327 (uchar
**)&g_opts
.dupscans
, (uchar
**)&g_opts
.dupscans
, 0,
1328 GET_UINT
, REQUIRED_ARG
, 10, 0, 0, 0, 0, 0 },
1329 { "keeptable", 1011, "Use existing table and data if any and do not drop",
1330 (uchar
**)&g_opts
.keeptable
, (uchar
**)&g_opts
.keeptable
, 0,
1331 GET_BOOL
, NO_ARG
, 0, 0, 0, 0, 0, 0 },
1332 { "no-extra-checks", 1012, "Omit expensive consistency checks",
1333 (uchar
**)&g_opts
.nochecks
, (uchar
**)&g_opts
.nochecks
, 0,
1334 GET_BOOL
, NO_ARG
, 0, 0, 0, 0, 0, 0 },
1335 { "abort-on-error", 1013, "Dump core on any error",
1336 (uchar
**)&g_opts
.abort
, (uchar
**)&g_opts
.abort
, 0,
1337 GET_BOOL
, NO_ARG
, 0, 0, 0, 0, 0, 0 },
1340 GET_NO_ARG
, NO_ARG
, 0, 0, 0, 0, 0, 0 }
1348 << ": measure records_in_range error as percentage of total rows" << endl
;
1349 my_print_help(my_long_options
);
1355 chkrc(g_opts
.rows
!= 0);
1356 chkrc(g_opts
.nullkeys
<= 100);
1357 chkrc(g_opts
.dupkeys
>= 100);
1358 chkrc(g_opts
.scanpct
<= 100);
1359 chkrc(g_opts
.eqscans
<= 100);
1360 chkrc(g_opts
.dupscans
<= 100);
1367 g_ncc
= new Ndb_cluster_connection();
1368 chkdb(g_ncc
->connect(30) == 0);
1369 g_ndb
= new Ndb(g_ncc
, "TEST_DB");
1370 chkdb(g_ndb
->init() == 0 && g_ndb
->waitUntilReady(30) == 0);
1385 main(int argc
, char** argv
)
1388 const char* g_progname
=
1389 strchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
1391 ndbout
<< g_progname
;
1392 for (i
= 1; i
< argc
; i
++)
1393 ndbout
<< " " << argv
[i
];
1396 ret
= handle_options(&argc
, &argv
, my_long_options
, ndb_std_get_one_option
);
1397 if (ret
!= 0 || argc
!= 0)
1398 return NDBT_ProgramExit(NDBT_WRONGARGS
);
1399 if (checkoptions() == 0 && doconnect() == 0 && runtest() == 0) {
1401 return NDBT_ProgramExit(NDBT_OK
);
1404 return NDBT_ProgramExit(NDBT_FAILED
);