1 /*
2    Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <ndb_global.h>
26 #include <ndb_opts.h>
27 #include <NDBT.hpp>
28 #include <NdbApi.hpp>
29 #include <HugoTransactions.hpp>
30 #include <Bitmask.hpp>
31 #include <Vector.hpp>
32 
33 static const char* _dbname = "TEST_DB";
34 static int g_loops = 7;
35 
36 struct my_option my_long_options[] =
37 {
38   NDB_STD_OPTS("ndb_desc"),
39   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
40 };
41 
42 static const NdbDictionary::Table* create_random_table(Ndb*);
43 static int transactions(Ndb*, const NdbDictionary::Table* tab);
44 static int unique_indexes(Ndb*, const NdbDictionary::Table* tab);
45 static int ordered_indexes(Ndb*, const NdbDictionary::Table* tab);
46 static int node_restart(Ndb*, const NdbDictionary::Table* tab);
47 static int system_restart(Ndb*, const NdbDictionary::Table* tab);
48 static int testBitmask();
49 
50 int
main(int argc,char ** argv)51 main(int argc, char** argv){
52   NDB_INIT(argv[0]);
53   const char *load_default_groups[]= { "mysql_cluster",0 };
54   load_defaults("my",load_default_groups,&argc,&argv);
55   int ho_error;
56 
57   if ((ho_error=handle_options(&argc, &argv, my_long_options,
58 			       ndb_std_get_one_option)))
59     return NDBT_ProgramExit(NDBT_WRONGARGS);
60 
61   int res = NDBT_FAILED;
62 
63   /* Run cluster-independent tests */
64   for (int i=0; i<(10*g_loops); i++)
65   {
66     if (NDBT_OK != (res= testBitmask()))
67       return NDBT_ProgramExit(res);
68   }
69 
70   Ndb_cluster_connection con(opt_ndb_connectstring, opt_ndb_nodeid);
71   if(con.connect(12, 5, 1))
72   {
73     return NDBT_ProgramExit(NDBT_FAILED);
74   }
75 
76 
77   Ndb* pNdb;
78   pNdb = new Ndb(&con, _dbname);
79   pNdb->init();
80   while (pNdb->waitUntilReady() != 0) {};
81 
82   NdbDictionary::Dictionary * dict = pNdb->getDictionary();
83 
84   const NdbDictionary::Table* pTab = 0;
85   for (int i = 0; i < (argc ? argc : g_loops) ; i++)
86   {
87     res = NDBT_FAILED;
88     if(argc == 0)
89     {
90       pTab = create_random_table(pNdb);
91     }
92     else
93     {
94       dict->dropTable(argv[i]);
95       NDBT_Tables::createTable(pNdb, argv[i]);
96       pTab = dict->getTable(argv[i]);
97     }
98 
99     if (pTab == 0)
100     {
101       ndbout << "Failed to create table" << endl;
102       ndbout << dict->getNdbError() << endl;
103       break;
104     }
105 
106     if(transactions(pNdb, pTab))
107       break;
108 
109     if(unique_indexes(pNdb, pTab))
110       break;
111 
112     if(ordered_indexes(pNdb, pTab))
113       break;
114 
115     if(node_restart(pNdb, pTab))
116       break;
117 
118     if(system_restart(pNdb, pTab))
119       break;
120 
121     dict->dropTable(pTab->getName());
122     res = NDBT_OK;
123   }
124 
125   if(res != NDBT_OK && pTab)
126   {
127     dict->dropTable(pTab->getName());
128   }
129 
130   delete pNdb;
131   return NDBT_ProgramExit(res);
132 }
133 
134 static
135 const NdbDictionary::Table*
create_random_table(Ndb * pNdb)136 create_random_table(Ndb* pNdb)
137 {
138   do {
139     NdbDictionary::Table tab;
140     Uint32 cols = 1 + (rand() % (NDB_MAX_ATTRIBUTES_IN_TABLE - 1));
141     const Uint32 maxLength = 4090;
142     Uint32 length = maxLength;
143     Uint8  defbuf[(maxLength + 7)/8];
144 
145     BaseString name;
146     name.assfmt("TAB_%d", rand() & 65535);
147     tab.setName(name.c_str());
148     for(Uint32 i = 0; i<cols && length > 2; i++)
149     {
150       NdbDictionary::Column col;
151       name.assfmt("COL_%d", i);
152       col.setName(name.c_str());
153       if(i == 0 || i == 1)
154       {
155 	col.setType(NdbDictionary::Column::Unsigned);
156 	col.setLength(1);
157 	col.setNullable(false);
158 	col.setPrimaryKey(i == 0);
159 	tab.addColumn(col);
160 	continue;
161       }
162 
163       col.setType(NdbDictionary::Column::Bit);
164 
165       Uint32 len = 1 + (rand() % (length - 1));
166       memset(defbuf, 0, (length + 7)/8);
167       for (Uint32 j = 0; j < len/8; j++)
168         defbuf[j] = 0x63;
169       col.setDefaultValue(defbuf, (len + 7)/8);
170       col.setLength(len); length -= len;
171       int nullable = (rand() >> 16) & 1;
172       col.setNullable(nullable); length -= nullable;
173       col.setPrimaryKey(false);
174       tab.addColumn(col);
175     }
176 
177     pNdb->getDictionary()->dropTable(tab.getName());
178     if(pNdb->getDictionary()->createTable(tab) == 0)
179     {
180       ndbout << (NDBT_Table&)tab << endl;
181       return pNdb->getDictionary()->getTable(tab.getName());
182     }
183   } while(0);
184   return 0;
185 }
186 
187 static
188 int
transactions(Ndb * pNdb,const NdbDictionary::Table * tab)189 transactions(Ndb* pNdb, const NdbDictionary::Table* tab)
190 {
191   int i = 0;
192   HugoTransactions trans(* tab);
193   i |= trans.loadTable(pNdb, 1000);
194   i |= trans.pkReadRecords(pNdb, 1000, 13);
195   i |= trans.scanReadRecords(pNdb, 1000, 25);
196   i |= trans.pkUpdateRecords(pNdb, 1000, 37);
197   i |= trans.scanUpdateRecords(pNdb, 1000, 25);
198   i |= trans.pkDelRecords(pNdb, 500, 23);
199   i |= trans.clearTable(pNdb);
200   return i;
201 }
202 
203 static
204 int
unique_indexes(Ndb * pNdb,const NdbDictionary::Table * tab)205 unique_indexes(Ndb* pNdb, const NdbDictionary::Table* tab)
206 {
207   return 0;
208 }
209 
210 static
211 int
ordered_indexes(Ndb * pNdb,const NdbDictionary::Table * tab)212 ordered_indexes(Ndb* pNdb, const NdbDictionary::Table* tab)
213 {
214   return 0;
215 }
216 
217 static
218 int
node_restart(Ndb * pNdb,const NdbDictionary::Table * tab)219 node_restart(Ndb* pNdb, const NdbDictionary::Table* tab)
220 {
221   return 0;
222 }
223 
224 static
225 int
system_restart(Ndb * pNdb,const NdbDictionary::Table * tab)226 system_restart(Ndb* pNdb, const NdbDictionary::Table* tab)
227 {
228   return 0;
229 }
230 
231 /* Note : folowing classes test functionality of storage/ndb/src/common/util/Bitmask.cpp
232  * and were originally defined there.
233  * Set BITMASK_DEBUG to 1 to get more test debugging info.
234  */
235 #define BITMASK_DEBUG 0
236 
237 static
cmp(const Uint32 b1[],const Uint32 b2[],Uint32 len)238 bool cmp(const Uint32 b1[], const Uint32 b2[], Uint32 len)
239 {
240   Uint32 sz32 = (len + 31) >> 5;
241   for(Uint32 i = 0; i<len; i++)
242   {
243     if(BitmaskImpl::get(sz32, b1, i) ^ BitmaskImpl::get(sz32, b2, i))
244       return false;
245   }
246   return true;
247 }
248 
249 static
print(const Uint32 src[],Uint32 len,Uint32 pos=0)250 void print(const Uint32 src[], Uint32 len, Uint32 pos = 0)
251 {
252   printf("b'");
253   for(unsigned i = 0; i<len; i++)
254   {
255     if(BitmaskImpl::get((pos + len + 31) >> 5, src, i+pos))
256       printf("1");
257     else
258       printf("0");
259     if((i & 31) == 31)
260       printf(" ");
261   }
262 }
263 
lrand()264 static int lrand()
265 {
266   return rand();
267 }
268 
269 static
rand(Uint32 dst[],Uint32 len)270 void rand(Uint32 dst[], Uint32 len)
271 {
272   for(Uint32 i = 0; i<len; i++)
273     BitmaskImpl::set((len + 31) >> 5, dst, i, (lrand() % 1000) > 500);
274 }
275 
276 static
checkCopyField(const Uint32 totalTests)277 int checkCopyField(const Uint32 totalTests)
278 {
279   ndbout << "Testing : Checking Bitmaskimpl::copyField";
280 
281   const Uint32 numWords= 95;
282   const Uint32 maxBitsToCopy= (numWords * 32);
283 
284   Uint32 sourceBuf[numWords];
285   Uint32 targetTest[numWords];
286   Uint32 targetCopy[numWords];
287 
288   rand(sourceBuf, maxBitsToCopy);
289 
290   /* Set both target buffers to the same random values */
291   rand(targetTest, maxBitsToCopy);
292   for (Uint32 i=0; i<maxBitsToCopy; i++)
293     BitmaskImpl::set(numWords, targetCopy, i,
294                      BitmaskImpl::get(numWords, targetTest, i));
295 
296   if (!cmp(targetTest, targetCopy, maxBitsToCopy))
297   {
298     ndbout_c("copyField :: Initial setup mismatch");
299     return -1;
300   }
301 
302   for (Uint32 test=0; test < totalTests; test++)
303   {
304     Uint32 len= rand() % maxBitsToCopy;
305     Uint32 slack= maxBitsToCopy - len;
306     Uint32 srcPos= slack ? rand() % slack : 0;
307     Uint32 dstPos= slack ? rand() % slack : 0;
308 
309     if (BITMASK_DEBUG)
310       ndbout_c("copyField :: Running test with len=%u, srcPos=%u, dstPos=%u, "
311                "srcOff=%u, dstOff=%u",
312                len, srcPos, dstPos, srcPos & 31, dstPos & 31);
313 
314     /* Run the copy */
315     BitmaskImpl::copyField(targetCopy, dstPos, sourceBuf, srcPos, len);
316 
317     /* Do the equivalent action */
318     for (Uint32 i=0; i< len; i++)
319       BitmaskImpl::set(numWords, targetTest, dstPos + i,
320                        BitmaskImpl::get(numWords, sourceBuf, srcPos+i));
321 
322     bool fail= false;
323     /* Compare results */
324     for (Uint32 i=0; i<maxBitsToCopy; i++)
325     {
326       if (BitmaskImpl::get(numWords, targetCopy, i) !=
327           BitmaskImpl::get(numWords, targetTest, i))
328       {
329         ndbout_c("copyField :: Mismatch at bit %u, should be %u but is %u",
330                  i,
331                  BitmaskImpl::get(numWords, targetTest, i),
332                  BitmaskImpl::get(numWords, targetCopy, i));
333         fail=true;
334       }
335     }
336 
337     if (fail)
338       return -1;
339   }
340 
341   return 0;
342 }
343 
344 static
checkNoTramplingGetSetField(const Uint32 totalTests)345 int checkNoTramplingGetSetField(const Uint32 totalTests)
346 {
347   const Uint32 numWords= 67;
348   const Uint32 maxBitsToCopy= (numWords * 32);
349   Uint32 sourceBuf[numWords];
350   Uint32 targetBuf[numWords];
351 
352   ndbout << "Testing : Bitmask NoTrampling\n";
353 
354   memset(sourceBuf, 0x00, (numWords*4));
355 
356   for (Uint32 test=0; test<totalTests; test++)
357   {
358     /* Always copy at least 1 bit */
359     Uint32 srcStart= rand() % (maxBitsToCopy -1);
360     Uint32 length= (rand() % ((maxBitsToCopy -1) - srcStart)) + 1;
361 
362     if (BITMASK_DEBUG)
363       ndbout << "Testing start %u, length %u \n"
364              << srcStart
365              << length;
366     // Set target to all ones.
367     memset(targetBuf, 0xff, (numWords*4));
368 
369     BitmaskImpl::getField(numWords, sourceBuf, srcStart, length, targetBuf);
370 
371     // Check that there is no trampling
372     Uint32 firstUntrampledWord= (length + 31)/32;
373 
374     for (Uint32 word=0; word< numWords; word++)
375     {
376       Uint32 targetWord= targetBuf[word];
377       if (BITMASK_DEBUG)
378         ndbout << "word=%d, targetWord=%u, firstUntrampledWord..=%u"
379                << word << targetWord << firstUntrampledWord;
380 
381       if (! (word < firstUntrampledWord) ?
382           (targetWord == 0) :
383           (targetWord == 0xffffffff))
384       {
385         ndbout << "Notrampling getField failed for srcStart "
386                << srcStart
387                << " length " << length
388                << " at word " << word << "\n";
389         ndbout << "word=%d, targetWord=%u, firstUntrampledWord..=%u"
390                << word << targetWord << firstUntrampledWord;
391         return -1;
392       }
393 
394     }
395 
396     /* Set target back to all ones. */
397     memset(targetBuf, 0xff, (numWords*4));
398 
399     BitmaskImpl::setField(numWords, targetBuf, srcStart, length, sourceBuf);
400 
401     /* Check we've got all ones, with zeros only where expected */
402     for (Uint32 word=0; word< numWords; word++)
403     {
404       Uint32 targetWord= targetBuf[word];
405 
406       for (Uint32 bit=0; bit< 32; bit++)
407       {
408         Uint32 bitNum= (word << 5) + bit;
409         bool expectedValue= !((bitNum >= srcStart) &&
410                               (bitNum < (srcStart + length)));
411         bool actualValue= (((targetWord >> bit) & 1) == 1);
412         if (BITMASK_DEBUG)
413           ndbout << "bitNum=%u expectedValue=%u, actual value=%u"
414                  << bitNum << expectedValue << actualValue;
415 
416         if (actualValue != expectedValue)
417         {
418           ndbout << "Notrampling setField failed for srcStart "
419                  << srcStart
420                  << " length " << length
421                  << " at word " << word << " bit " << bit <<  "\n";
422           ndbout << "bitNum=%u expectedValue=%u, actual value=%u"
423                  << bitNum << expectedValue << actualValue;
424           return -1;
425         }
426       }
427     }
428 
429   }
430 
431   return 0;
432 }
433 
434 static
simple(int pos,int size)435 int simple(int pos, int size)
436 {
437   ndbout << "Testing : Bitmask simple pos: " << pos << " size: " << size << "\n";
438   Vector<Uint32> _mask;
439   Vector<Uint32> _src;
440   Vector<Uint32> _dst;
441   Uint32 sz32 = (size + pos + 32) >> 5;
442   const Uint32 sz = 4 * sz32;
443 
444   Uint32 zero = 0;
445   _mask.fill(sz32+1, zero);
446   _src.fill(sz32+1, zero);
447   _dst.fill(sz32+1, zero);
448 
449   Uint32 * src = _src.getBase();
450   Uint32 * dst = _dst.getBase();
451   Uint32 * mask = _mask.getBase();
452 
453   memset(src, 0x0, sz);
454   memset(dst, 0x0, sz);
455   memset(mask, 0xFF, sz);
456   rand(src, size);
457   BitmaskImpl::setField(sz32, mask, pos, size, src);
458   BitmaskImpl::getField(sz32, mask, pos, size, dst);
459   if (BITMASK_DEBUG)
460   {
461     printf("src: "); print(src, size+31); printf("\n");
462     printf("msk: "); print(mask, (sz32 << 5) + 31); printf("\n");
463     printf("dst: "); print(dst, size+31); printf("\n");
464   }
465   return (cmp(src, dst, size+31)?0 : -1);
466 };
467 
468 struct Alloc
469 {
470   Uint32 pos;
471   Uint32 size;
472   Vector<Uint32> data;
473 };
474 
475 static
476 int
testRanges(Uint32 bitmask_size)477 testRanges(Uint32 bitmask_size)
478 {
479   Vector<Alloc> alloc_list;
480   bitmask_size = (bitmask_size + 31) & ~31;
481   Uint32 sz32 = (bitmask_size >> 5);
482   Vector<Uint32> alloc_mask;
483   Vector<Uint32> test_mask;
484 
485   ndbout_c("Testing : Bitmask ranges for bitmask of size %d", bitmask_size);
486   Uint32 zero = 0;
487   alloc_mask.fill(sz32, zero);
488   test_mask.fill(sz32, zero);
489 
490   /* Loop a number of times, setting and clearing bits in the mask
491    * and tracking the modifications in a separate structure.
492    * Check that both structures remain in sync
493    */
494   for(int i = 0; i<5000; i++)
495   {
496     Vector<Uint32> tmp;
497     tmp.fill(sz32, zero);
498 
499     Uint32 pos = lrand() % (bitmask_size - 1);
500     Uint32 free = 0;
501     if(BitmaskImpl::get(sz32, alloc_mask.getBase(), pos))
502     {
503       // Bit was allocated
504       // 1) Look up allocation
505       // 2) Check data
506       // 3) free it
507       size_t j;
508       Uint32 min, max;
509       for(j = 0; j<alloc_list.size(); j++)
510       {
511 	min = alloc_list[j].pos;
512 	max = min + alloc_list[j].size;
513 	if(pos >= min && pos < max)
514 	{
515 	  break;
516 	}
517       }
518       if (! ((pos >= min) && (pos < max)))
519       {
520         printf("Failed with pos %u, min %u, max %u\n",
521                pos, min, max);
522         return -1;
523       }
524       BitmaskImpl::getField(sz32, test_mask.getBase(), min, max-min,
525 			    tmp.getBase());
526       if(BITMASK_DEBUG)
527       {
528 	printf("freeing [ %d %d ]", min, max);
529 	printf("- mask: ");
530 	print(tmp.getBase(), max - min);
531 
532 	printf(" save: ");
533 	size_t k;
534 	Alloc& a = alloc_list[j];
535 	for(k = 0; k<a.data.size(); k++)
536 	  printf("%.8x ", a.data[k]);
537 	printf("\n");
538       }
539       if(!cmp(tmp.getBase(), alloc_list[j].data.getBase(), max - min))
540       {
541 	return -1;
542       }
543       while(min < max)
544 	BitmaskImpl::clear(sz32, alloc_mask.getBase(), min++);
545       alloc_list.erase(j);
546     }
547     else
548     {
549       Vector<Uint32> tmp;
550       tmp.fill(sz32, zero);
551 
552       // Bit was free
553       // 1) Check how much space is avaiable
554       // 2) Create new allocation of lrandom size
555       // 3) Fill data with lrandom data
556       // 4) Update alloc mask
557       while(pos+free < bitmask_size &&
558 	    !BitmaskImpl::get(sz32, alloc_mask.getBase(), pos+free))
559 	free++;
560 
561       Uint32 sz =
562 	(free <= 64 && ((lrand() % 100) > 80)) ? free : (lrand() % free);
563       sz = sz ? sz : 1;
564       sz = pos + sz == bitmask_size ? sz - 1 : sz;
565       Alloc a;
566       a.pos = pos;
567       a.size = sz;
568       a.data.fill(((sz+31)>> 5)-1, zero);
569       if(BITMASK_DEBUG)
570 	printf("pos %d -> alloc [ %d %d ]", pos, pos, pos+sz);
571       for(size_t j = 0; j<sz; j++)
572       {
573 	BitmaskImpl::set(sz32, alloc_mask.getBase(), pos+j);
574 	if((lrand() % 1000) > 500)
575 	  BitmaskImpl::set((sz + 31) >> 5, a.data.getBase(), j);
576       }
577       if(BITMASK_DEBUG)
578       {
579 	printf("- mask: ");
580 	print(a.data.getBase(), sz);
581 	printf("\n");
582       }
583       BitmaskImpl::setField(sz32, test_mask.getBase(), pos, sz,
584 			    a.data.getBase());
585       alloc_list.push_back(a);
586     }
587   }
588 
589 #define NDB_BM_SUPPORT_RANGE
590 #ifdef NDB_BM_SUPPORT_RANGE
591   for(Uint32 i = 0; i<1000; i++)
592   {
593     Uint32 sz32 = 10+rand() % 100;
594     Uint32 zero = 0;
595     Vector<Uint32> map;
596     map.fill(sz32, zero);
597 
598     Uint32 sz = 32 * sz32;
599     Uint32 start = (rand() % sz);
600     Uint32 stop = start + ((rand() % (sz - start)) & 0xFFFFFFFF);
601 
602     Vector<Uint32> check;
603     check.fill(sz32, zero);
604 
605     /* Verify range setting method works correctly */
606     for(Uint32 j = 0; j<sz; j++)
607     {
608       bool expect = (j >= start && j<stop);
609       if(expect)
610 	BitmaskImpl::set(sz32, check.getBase(), j);
611     }
612 
613     BitmaskImpl::setRange(sz32, map.getBase(), start, stop - start + 1);
614     if (!BitmaskImpl::equal(sz32, map.getBase(), check.getBase()))
615     {
616       ndbout_c(" FAIL 1 sz: %d [ %d %d ]", sz, start, stop);
617       printf("check: ");
618       for(Uint32 j = 0; j<sz32; j++)
619 	printf("%.8x ", check[j]);
620       printf("\n");
621 
622       printf("map  : ");
623       for(Uint32 j = 0; j<sz32; j++)
624 	printf("%.8x ", map[j]);
625       printf("\n");
626       return -1;
627     }
628 
629     map.clear();
630     check.clear();
631 
632     /* Verify range clearing method works correctly */
633     Uint32 one = ~(Uint32)0;
634     map.fill(sz32, one);
635     check.fill(sz32, one);
636 
637     for(Uint32 j = 0; j<sz; j++)
638     {
639       bool expect = (j >= start && j<stop);
640       if(expect)
641 	BitmaskImpl::clear(sz32, check.getBase(), j);
642     }
643 
644     BitmaskImpl::clear_range(sz32, map.getBase(), start, stop);
645     if (!BitmaskImpl::equal(sz32, map.getBase(), check.getBase()))
646     {
647       ndbout_c(" FAIL 2 sz: %d [ %d %d ]", sz, start, stop);
648       printf("check: ");
649       for(Uint32 j = 0; j<sz32; j++)
650 	printf("%.8x ", check[j]);
651       printf("\n");
652 
653       printf("map  : ");
654       for(Uint32 j = 0; j<sz32; j++)
655 	printf("%.8x ", map[j]);
656       printf("\n");
657       return -1;
658     }
659   }
660 #endif
661 
662   return 0;
663 }
664 
665 static
666 int
testBitmask()667 testBitmask()
668 {
669   /* Some testcases from storage/ndb/src/common/util/Bitmask.cpp */
670   int res= 0;
671 
672   if ((res= checkNoTramplingGetSetField(100 /* totalTests */)) != 0)
673     return res;
674 
675   if ((res= checkCopyField(1000)) != 0)
676     return res;
677 
678   if ((res= simple(rand() % 33, // position
679                    (rand() % 63)+1) // size
680        ) != 0)
681     return res;
682 
683   if ((res= testRanges(1+(rand() % 1000) // bitmask size
684                        )) != 0)
685     return res;
686 
687   return 0;
688 }
689 
690 template class Vector<Alloc>;
691