1 /*
2    Copyright (c) 2003, 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 "HugoCalculator.hpp"
27 #include <NDBT.hpp>
28 
29 static
30 Uint32
myRand(Uint64 * seed)31 myRand(Uint64 * seed)
32 {
33   const Uint64 mul= 0x5deece66dull;
34   const Uint64 add= 0xb;
35   Uint64 loc_result = *seed * mul + add;
36 
37   * seed= loc_result;
38   return (Uint32)(loc_result >> 1);
39 }
40 
41 static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
42                              "abcdefghijklmnopqrstuvwxyz"
43                              "0123456789+/";
44 
45 /* *************************************************************
46  * HugoCalculator
47  *
48  *  Comon class for the Hugo test suite, provides the functions
49  *  that is used for calculating values to load in to table and
50  *  also knows how to verify a row that's been read from db
51  *
52  * ************************************************************/
HugoCalculator(const NdbDictionary::Table & tab)53 HugoCalculator::HugoCalculator(const NdbDictionary::Table& tab) : m_tab(tab) {
54 
55   // The "id" column of this table is found in the first integer column
56   int i;
57   for (i=0; i<m_tab.getNoOfColumns(); i++){
58     const NdbDictionary::Column* attr = m_tab.getColumn(i);
59     if (attr->getType() == NdbDictionary::Column::Unsigned){
60       m_idCol = i;
61       break;
62     }
63   }
64 
65   // The "number of updates" column for this table is found in the last column
66   for (i=m_tab.getNoOfColumns()-1; i>=0; i--){
67     const NdbDictionary::Column* attr = m_tab.getColumn(i);
68     if (attr->getType() == NdbDictionary::Column::Unsigned &&
69 	!attr->getPrimaryKey()){
70       m_updatesCol = i;
71       break;
72     }
73   }
74 #if 0
75   ndbout << "idCol = " << m_idCol << endl;
76   ndbout << "updatesCol = " << m_updatesCol << endl;
77 #endif
78   // Check that idCol is not conflicting with updatesCol
79   assert(m_idCol != m_updatesCol && m_idCol != -1 && m_updatesCol != -1);
80 }
81 
82 Int32
calcValue(int record,int attrib,int updates) const83 HugoCalculator::calcValue(int record,
84 			  int attrib,
85 			  int updates) const {
86 
87   Int32 i;
88   Uint32 j;
89   calcValue(record, attrib, updates, (char*)&i, sizeof(i), &j);
90 
91   return i;
92 }
93 #if 0
94 HugoCalculator::U_Int32 calcValue(int record, int attrib, int updates) const;
95 HugoCalculator::U_Int64 calcValue(int record, int attrib, int updates) const;
96 HugoCalculator::Int64 calcValue(int record, int attrib, int updates) const;
97 HugoCalculator::float calcValue(int record, int attrib, int updates) const;
98 HugoCalculator::double calcValue(int record, int attrib, int updates) const;
99 #endif
100 
101 static
102 Uint32
calc_len(Uint32 rvalue,int maxlen)103 calc_len(Uint32 rvalue, int maxlen)
104 {
105   Uint32 minlen = 25;
106 
107   if ((rvalue >> 16) < 4096)
108     minlen = 15;
109   else if ((rvalue >> 16) < 8192)
110     minlen = 25;
111   else if ((rvalue >> 16) < 16384)
112     minlen = 35;
113   else
114     minlen = 64;
115 
116   if ((Uint32)maxlen <= minlen)
117     return maxlen;
118 
119   /* Ensure coverage of maxlen */
120   if ((rvalue & 64) == 0)
121     return maxlen;
122 
123   return minlen + (rvalue % (maxlen - minlen));
124 }
125 
126 static
127 Uint32
calc_blobLen(Uint32 rvalue,int maxlen)128 calc_blobLen(Uint32 rvalue, int maxlen)
129 {
130   int minlen = 1000;
131 
132   if ((rvalue >> 16) < 4096)
133     minlen = 5000;
134   else if ((rvalue >> 16) < 8192)
135     minlen = 8000;
136   else if ((rvalue >> 16) < 16384)
137     minlen = 12000;
138   else
139     minlen = 16000;
140 
141   if (maxlen <= minlen)
142     return maxlen;
143 
144   return minlen + (rvalue % (maxlen - minlen));
145 }
146 
147 const char*
calcValue(int record,int attrib,int updates,char * buf,int len,Uint32 * outlen) const148 HugoCalculator::calcValue(int record,
149 			  int attrib,
150 			  int updates,
151 			  char* buf,
152 			  int len,
153 			  Uint32 *outlen) const {
154   Uint64 seed;
155   const NdbDictionary::Column* attr = m_tab.getColumn(attrib);
156   Uint32 val;
157   * outlen = len;
158   do
159   {
160     if (attrib == m_idCol)
161     {
162       val= record;
163       memcpy(buf, &val, 4);
164       return buf;
165     }
166 
167     // If this is the update column
168     if (attrib == m_updatesCol)
169     {
170       val= updates;
171       memcpy(buf, &val, 4);
172       return buf;
173     }
174 
175     if (attr->getPrimaryKey())
176     {
177       seed = record + attrib;
178     }
179     else
180     {
181       seed = record + attrib + updates;
182     }
183   } while (0);
184 
185   val = myRand(&seed);
186 
187   if(attr->getNullable() && (((val >> 16) & 255) > 220))
188   {
189     * outlen = 0;
190     return NULL;
191   }
192 
193   int pos= 0;
194   char* dst= buf;
195   switch(attr->getType()){
196   case NdbDictionary::Column::Tinyint:
197   case NdbDictionary::Column::Tinyunsigned:
198   case NdbDictionary::Column::Smallint:
199   case NdbDictionary::Column::Smallunsigned:
200   case NdbDictionary::Column::Mediumint:
201   case NdbDictionary::Column::Mediumunsigned:
202   case NdbDictionary::Column::Int:
203   case NdbDictionary::Column::Unsigned:
204   case NdbDictionary::Column::Bigint:
205   case NdbDictionary::Column::Bigunsigned:
206   case NdbDictionary::Column::Olddecimal:
207   case NdbDictionary::Column::Olddecimalunsigned:
208   case NdbDictionary::Column::Decimal:
209   case NdbDictionary::Column::Decimalunsigned:
210   case NdbDictionary::Column::Binary:
211   case NdbDictionary::Column::Datetime:
212   case NdbDictionary::Column::Time:
213   case NdbDictionary::Column::Date:
214   case NdbDictionary::Column::Bit:
215     while (len > 4)
216     {
217       memcpy(buf+pos, &val, 4);
218       pos += 4;
219       len -= 4;
220       val= myRand(&seed);
221     }
222 
223     memcpy(buf+pos, &val, len);
224     if(attr->getType() == NdbDictionary::Column::Bit)
225     {
226       Uint32 bits= attr->getLength();
227       Uint32 tmp = bits >> 5;
228       Uint32 size = bits & 31;
229       Uint32 copy;
230       memcpy(&copy, ((Uint32*)buf)+tmp, 4);
231       copy &= ((1 << size) - 1);
232       memcpy(((Uint32*)buf)+tmp, &copy, 4);
233     }
234     break;
235   case NdbDictionary::Column::Float:
236     {
237       float x = (float)myRand(&seed);
238       memcpy(buf+pos, &x, 4);
239       pos += 4;
240       len -= 4;
241     }
242     break;
243   case NdbDictionary::Column::Double:
244     {
245       double x = (double)myRand(&seed);
246       memcpy(buf+pos, &x, 8);
247       pos += 8;
248       len -= 8;
249     }
250     break;
251   case NdbDictionary::Column::Varbinary:
252   case NdbDictionary::Column::Varchar:
253     len = calc_len(myRand(&seed), len - 1);
254     assert(len < 256);
255     * outlen = len + 1;
256     * buf = len;
257     dst++;
258     goto write_char;
259   case NdbDictionary::Column::Longvarchar:
260   case NdbDictionary::Column::Longvarbinary:
261     len = calc_len(myRand(&seed), len - 2);
262     assert(len < 65536);
263     * outlen = len + 2;
264     int2store(buf, len);
265     dst += 2;
266 write_char:
267   case NdbDictionary::Column::Char:
268   {
269     char* ptr= (char*)&val;
270     while(len >= 4)
271     {
272       len -= 4;
273       dst[pos++] = base64_table[ptr[0] & 0x3f];
274       dst[pos++] = base64_table[ptr[1] & 0x3f];
275       dst[pos++] = base64_table[ptr[2] & 0x3f];
276       dst[pos++] = base64_table[ptr[3] & 0x3f];
277       val= myRand(&seed);
278     }
279 
280     for(; len; len--, pos++)
281       dst[pos] = base64_table[ptr[len] & 0x3f];
282 
283     pos--;
284     break;
285   }
286   case NdbDictionary::Column::Blob:
287     * outlen = calc_blobLen(myRand(&seed), len);
288     // Don't set any actual data...
289     break;
290   case NdbDictionary::Column::Undefined:
291   case NdbDictionary::Column::Text:
292   case NdbDictionary::Column::Year:
293   case NdbDictionary::Column::Timestamp:
294     abort();
295     break;
296   }
297 
298   return buf;
299 }
300 
301 int
verifyRowValues(NDBT_ResultRow * const pRow) const302 HugoCalculator::verifyRowValues(NDBT_ResultRow* const  pRow) const{
303   int id, updates;
304 
305   id = pRow->attributeStore(m_idCol)->u_32_value();
306   updates = pRow->attributeStore(m_updatesCol)->u_32_value();
307   int result = 0;
308 
309   // Check the values of each column
310   for (int i = 0; i<m_tab.getNoOfColumns(); i++){
311     if (i != m_updatesCol && id != m_idCol) {
312       const NdbDictionary::Column* attr = m_tab.getColumn(i);
313       Uint32 len = attr->getSizeInBytes(), real_len;
314       char buf[NDB_MAX_TUPLE_SIZE];
315       const char* res = calcValue(id, i, updates, buf, len, &real_len);
316       if (res == NULL){
317 	if (!pRow->attributeStore(i)->isNULL()){
318 	  g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl;
319 	  g_err << "|- The row: \"" << (*pRow) << "\"" << endl;
320 	  result = -1;
321 	}
322       } else{
323 	if (real_len != pRow->attributeStore(i)->get_size_in_bytes())
324 	{
325 	  g_err << "|- Invalid data found in attribute " << i << ": \""
326 		<< "Length of expected=" << real_len << endl
327 		<< "Lenght of read="
328 		<< pRow->attributeStore(i)->get_size_in_bytes() << endl;
329 	  result= -1;
330 	}
331 	else if (memcmp(res, pRow->attributeStore(i)->aRef(), real_len) != 0)
332 	{
333 	  g_err << "Column: " << attr->getName() << endl;
334 	  const char* buf2 = pRow->attributeStore(i)->aRef();
335 	  for (Uint32 j = 0; j < len; j++)
336 	  {
337 	    g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)buf2[j] << "]";
338 	    if (buf[j] != buf2[j])
339 	    {
340 	      g_err << "==>Match failed!";
341 	    }
342 	    g_err << endl;
343 	  }
344 	  g_err << endl;
345 	  g_err << "|- Invalid data found in attribute " << i << ": \""
346 		<< pRow->attributeStore(i)->aRef()
347 		<< "\" != \"" << res << "\"" << endl
348 		<< "Length of expected=" << (unsigned)strlen(res) << endl
349 		<< "Lenght of read="
350 		<< pRow->attributeStore(i)->get_size_in_bytes() << endl;
351 	  g_err << "|- The row: \"" << (* pRow) << "\"" << endl;
352 	  result = -1;
353 	}
354       }
355     }
356   }
357   return result;
358 }
359 
360 
361 int
verifyRecAttr(int record,int updates,const NdbRecAttr * recAttr)362 HugoCalculator::verifyRecAttr(int record,
363                               int updates,
364                               const NdbRecAttr* recAttr)
365 {
366   const char* valPtr = NULL;
367   int attrib = recAttr->getColumn()->getAttrId();
368   Uint32 valLen = recAttr->get_size_in_bytes();
369   if (!recAttr->isNULL())
370     valPtr= (const char*) recAttr->aRef();
371 
372   return verifyColValue(record,
373                         attrib,
374                         updates,
375                         valPtr,
376                         valLen);
377 }
378 
379 int
verifyColValue(int record,int attrib,int updates,const char * valPtr,Uint32 valLen)380 HugoCalculator::verifyColValue(int record,
381                                int attrib,
382                                int updates,
383                                const char* valPtr,
384                                Uint32 valLen)
385 {
386   int result = 0;
387 
388   if (attrib == m_updatesCol)
389   {
390     int val= *((const int*) valPtr);
391     if (val != updates)
392     {
393       g_err << "|- Updates column (" << attrib << ")" << endl;
394       g_err << "|- Expected " << updates << " but found " << val << endl;
395       result = -1;
396     }
397   }
398   else if (attrib == m_idCol)
399   {
400     int val= *((const int*) valPtr);
401     if (val != record)
402     {
403       g_err << "|- Identity column (" << attrib << ")" << endl;
404       g_err << "|- Expected " << record << " but found " << val << endl;
405       result = -1;
406     }
407   }
408   else
409   {
410     /* 'Normal' data column */
411     const NdbDictionary::Column* attr = m_tab.getColumn(attrib);
412     Uint32 len = attr->getSizeInBytes(), real_len;
413     char buf[NDB_MAX_TUPLE_SIZE];
414     const char* res = calcValue(record, attrib, updates, buf, len, &real_len);
415     if (res == NULL){
416       if (valPtr != NULL){
417         g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl;
418         g_err << "|- Column length is " << valLen << " bytes" << endl;
419         g_err << "|- Column data follows :" << endl;
420         for (Uint32 j = 0; j < valLen; j ++)
421         {
422           g_err << j << ":" << hex << (Uint32)(Uint8)valPtr[j] << endl;
423         }
424         result = -1;
425       }
426     } else{
427       if (real_len != valLen)
428       {
429         g_err << "|- Invalid data found in attribute " << attrib << ": \""
430               << "Length of expected=" << real_len << endl
431               << "Length of passed="
432               << valLen << endl;
433         result= -1;
434       }
435       else if (memcmp(res, valPtr, real_len) != 0)
436       {
437         g_err << "|- Expected data mismatch on column "
438               << attr->getName() << " length " << real_len
439               << " bytes " << endl;
440         g_err << "|- Bytewise comparison follows :" << endl;
441         for (Uint32 j = 0; j < real_len; j++)
442         {
443           g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)valPtr[j] << "]";
444           if (buf[j] != valPtr[j])
445           {
446             g_err << "==>Match failed!";
447           }
448           g_err << endl;
449         }
450         g_err << endl;
451         result = -1;
452       }
453     }
454   }
455 
456   return result;
457 }
458 
459 int
getIdValue(NDBT_ResultRow * const pRow) const460 HugoCalculator::getIdValue(NDBT_ResultRow* const pRow) const {
461   return pRow->attributeStore(m_idCol)->u_32_value();
462 }
463 
464 int
getUpdatesValue(NDBT_ResultRow * const pRow) const465 HugoCalculator::getUpdatesValue(NDBT_ResultRow* const pRow) const {
466   return pRow->attributeStore(m_updatesCol)->u_32_value();
467 }
468 
469 int
equalForRow(Uint8 * pRow,const NdbRecord * pRecord,int rowId)470 HugoCalculator::equalForRow(Uint8 * pRow,
471                             const NdbRecord* pRecord,
472                             int rowId)
473 {
474   for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
475   {
476     const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
477 
478     if (attr->getPrimaryKey() == true)
479     {
480       char buf[8000];
481       int len = attr->getSizeInBytes();
482       memset(buf, 0, sizeof(buf));
483       Uint32 real_len;
484       const char * value = calcValue(rowId, attrId, 0, buf,
485                                      len, &real_len);
486       assert(value != 0); // NULLable PK not supported...
487       Uint32 off = 0;
488       bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
489       if (!ret)
490         abort();
491       memcpy(pRow + off, buf, real_len);
492     }
493   }
494   return NDBT_OK;
495 }
496 
497 int
setValues(Uint8 * pRow,const NdbRecord * pRecord,int rowId,int updateVal)498 HugoCalculator::setValues(Uint8 * pRow,
499                           const NdbRecord* pRecord,
500                           int rowId,
501                           int updateVal)
502 {
503   int res = equalForRow(pRow, pRecord, rowId);
504   if (res != 0)
505   {
506     return res;
507   }
508 
509   for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
510   {
511     const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
512 
513     if (attr->getPrimaryKey() == false)
514     {
515       char buf[8000];
516       int len = attr->getSizeInBytes();
517       memset(buf, 0, sizeof(buf));
518       Uint32 real_len;
519       const char * value = calcValue(rowId, attrId, updateVal, buf,
520                                      len, &real_len);
521       if (value != 0)
522       {
523         Uint32 off = 0;
524         bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
525         if (!ret)
526           abort();
527         memcpy(pRow + off, buf, real_len);
528         if (attr->getNullable())
529           NdbDictionary::setNull(pRecord, (char*)pRow, attrId, false);
530       }
531       else
532       {
533         assert(attr->getNullable());
534         NdbDictionary::setNull(pRecord, (char*)pRow, attrId, true);
535       }
536     }
537   }
538 
539   return NDBT_OK;
540 }
541