1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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  *  Common 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)
54   : m_tab(tab), m_idCol(-1), m_updatesCol(-1)
55 {
56   // The "id" column of this table is found in the first integer column
57   int i;
58   for (i=0; i<m_tab.getNoOfColumns(); i++){
59     const NdbDictionary::Column* attr = m_tab.getColumn(i);
60     if (attr->getType() == NdbDictionary::Column::Unsigned){
61       m_idCol = i;
62       break;
63     }
64   }
65 
66   // The "number of updates" column for this table is found in the last column
67   for (i=m_tab.getNoOfColumns()-1; i>=0; i--){
68     const NdbDictionary::Column* attr = m_tab.getColumn(i);
69     if (attr->getType() == NdbDictionary::Column::Unsigned &&
70 	!attr->getPrimaryKey()){
71       m_updatesCol = i;
72       break;
73     }
74   }
75 #if 0
76   ndbout << "idCol = " << m_idCol << endl;
77   ndbout << "updatesCol = " << m_updatesCol << endl;
78 #endif
79   // Check that idCol is not conflicting with updatesCol
80   require(m_idCol != -1);
81   require(m_updatesCol != -1);
82   require(m_idCol != m_updatesCol);
83 }
84 
85 Int32
calcValue(int record,int attrib,int updates) const86 HugoCalculator::calcValue(int record,
87 			  int attrib,
88 			  int updates) const {
89 
90   Int32 i;
91   Uint32 j;
92   calcValue(record, attrib, updates, (char*)&i, sizeof(i), &j);
93 
94   return i;
95 }
96 #if 0
97 HugoCalculator::U_Int32 calcValue(int record, int attrib, int updates) const;
98 HugoCalculator::U_Int64 calcValue(int record, int attrib, int updates) const;
99 HugoCalculator::Int64 calcValue(int record, int attrib, int updates) const;
100 HugoCalculator::float calcValue(int record, int attrib, int updates) const;
101 HugoCalculator::double calcValue(int record, int attrib, int updates) const;
102 #endif
103 
104 static
105 Uint32
calc_len(Uint32 rvalue,int maxlen)106 calc_len(Uint32 rvalue, int maxlen)
107 {
108   Uint32 minlen = 25;
109 
110   if ((rvalue >> 16) < 4096)
111     minlen = 15;
112   else if ((rvalue >> 16) < 8192)
113     minlen = 25;
114   else if ((rvalue >> 16) < 16384)
115     minlen = 35;
116   else
117     minlen = 64;
118 
119   if ((Uint32)maxlen <= minlen)
120     return maxlen;
121 
122   /* Ensure coverage of maxlen */
123   if ((rvalue & 64) == 0)
124     return maxlen;
125 
126   return minlen + (rvalue % (maxlen - minlen));
127 }
128 
129 static
130 Uint32
calc_blobLen(Uint32 rvalue,int maxlen)131 calc_blobLen(Uint32 rvalue, int maxlen)
132 {
133   int minlen = 1000;
134 
135   if ((rvalue >> 16) < 4096)
136     minlen = 5000;
137   else if ((rvalue >> 16) < 8192)
138     minlen = 8000;
139   else if ((rvalue >> 16) < 16384)
140     minlen = 12000;
141   else
142     minlen = 16000;
143 
144   if (maxlen <= minlen)
145     return maxlen;
146 
147   return minlen + (rvalue % (maxlen - minlen));
148 }
149 
150 const char*
calcValue(int record,int attrib,int updates,char * buf,int len,Uint32 * outlen) const151 HugoCalculator::calcValue(int record,
152 			  int attrib,
153 			  int updates,
154 			  char* buf,
155 			  int len,
156 			  Uint32 *outlen) const {
157   Uint64 seed;
158   const NdbDictionary::Column* attr = m_tab.getColumn(attrib);
159   Uint32 val;
160   * outlen = len;
161   do
162   {
163     if (attrib == m_idCol)
164     {
165       val= record;
166       memcpy(buf, &val, 4);
167       return buf;
168     }
169 
170     // If this is the update column
171     if (attrib == m_updatesCol)
172     {
173       val= updates;
174       memcpy(buf, &val, 4);
175       return buf;
176     }
177 
178     if (attr->getPrimaryKey())
179     {
180       seed = record + attrib;
181     }
182     else
183     {
184       seed = record + attrib + updates;
185     }
186   } while (0);
187 
188   val = myRand(&seed);
189 
190   if(attr->getNullable() && (((val >> 16) & 255) > 220))
191   {
192     * outlen = 0;
193     return NULL;
194   }
195 
196   int pos= 0;
197   char* dst= buf;
198   switch(attr->getType()){
199   case NdbDictionary::Column::Tinyint:
200   case NdbDictionary::Column::Tinyunsigned:
201   case NdbDictionary::Column::Smallint:
202   case NdbDictionary::Column::Smallunsigned:
203   case NdbDictionary::Column::Mediumint:
204   case NdbDictionary::Column::Mediumunsigned:
205   case NdbDictionary::Column::Int:
206   case NdbDictionary::Column::Unsigned:
207   case NdbDictionary::Column::Bigint:
208   case NdbDictionary::Column::Bigunsigned:
209   case NdbDictionary::Column::Olddecimal:
210   case NdbDictionary::Column::Olddecimalunsigned:
211   case NdbDictionary::Column::Decimal:
212   case NdbDictionary::Column::Decimalunsigned:
213   case NdbDictionary::Column::Binary:
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     require(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     require(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   /*
287    * Date and time types.  Compared as binary data so valid values
288    * are not required, but they can be nice for manual testing e.g.
289    * to avoid garbage in ndb_select_all output.  -todo
290    */
291   case NdbDictionary::Column::Year:
292   case NdbDictionary::Column::Date:
293   case NdbDictionary::Column::Time:
294   case NdbDictionary::Column::Datetime:
295   case NdbDictionary::Column::Time2:
296   case NdbDictionary::Column::Datetime2:
297   case NdbDictionary::Column::Timestamp:
298   case NdbDictionary::Column::Timestamp2:
299     while (len > 4)
300     {
301       memcpy(buf+pos, &val, 4);
302       pos += 4;
303       len -= 4;
304       val= myRand(&seed);
305     }
306     memcpy(buf+pos, &val, len);
307     break;
308   case NdbDictionary::Column::Blob:
309     * outlen = calc_blobLen(myRand(&seed), len);
310     // Don't set any actual data...
311     break;
312   case NdbDictionary::Column::Undefined:
313   case NdbDictionary::Column::Text:
314     abort();
315     break;
316   }
317 
318   return buf;
319 }
320 
321 int
verifyRowValues(NDBT_ResultRow * const pRow) const322 HugoCalculator::verifyRowValues(NDBT_ResultRow* const  pRow) const{
323   const int id = getIdValue(pRow);
324   const int updates = pRow->attributeStore(m_updatesCol)->u_32_value();
325   int result = 0;
326 
327   // Check the values of each column
328   for (int i = 0; i<m_tab.getNoOfColumns(); i++){
329     if (i != m_updatesCol && i != m_idCol) {
330       const NdbDictionary::Column* attr = m_tab.getColumn(i);
331       const Uint32 len = attr->getSizeInBytes();
332       Uint32 real_len;
333       char buf[NDB_MAX_TUPLE_SIZE];
334       const char* res = calcValue(id, i, updates, buf, len, &real_len);
335       if (res == NULL){
336 	if (!pRow->attributeStore(i)->isNULL()){
337 	  g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl;
338 	  g_err << "|- The row: \"" << (*pRow) << "\"" << endl;
339 	  result = -1;
340 	}
341       } else{
342 	if (real_len != pRow->attributeStore(i)->get_size_in_bytes())
343 	{
344 	  g_err << "|- Invalid data found in attribute " << i << ": \""
345 		<< "Length of expected=" << real_len << endl
346 		<< "Lenght of read="
347 		<< pRow->attributeStore(i)->get_size_in_bytes() << endl;
348 	  result= -1;
349 	}
350 	else if (memcmp(res, pRow->attributeStore(i)->aRef(), real_len) != 0)
351 	{
352 	  g_err << "Column: " << attr->getName() << endl;
353 	  const char* buf2 = pRow->attributeStore(i)->aRef();
354 	  for (Uint32 j = 0; j < len; j++)
355 	  {
356 	    g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)buf2[j] << "]";
357 	    if (buf[j] != buf2[j])
358 	    {
359 	      g_err << "==>Match failed!";
360 	    }
361 	    g_err << endl;
362 	  }
363 	  g_err << endl;
364 	  g_err << "|- Invalid data found in attribute " << i << ": \""
365 		<< pRow->attributeStore(i)->aRef()
366 		<< "\" != \"" << res << "\"" << endl
367 		<< "Length of expected=" << (unsigned)strlen(res) << endl
368 		<< "Lenght of read="
369 		<< pRow->attributeStore(i)->get_size_in_bytes() << endl;
370 	  g_err << "|- The row: \"" << (* pRow) << "\"" << endl;
371 	  result = -1;
372 	}
373       }
374     }
375   }
376   return result;
377 }
378 
379 
380 int
verifyRecAttr(int record,int updates,const NdbRecAttr * recAttr)381 HugoCalculator::verifyRecAttr(int record,
382                               int updates,
383                               const NdbRecAttr* recAttr)
384 {
385   const char* valPtr = NULL;
386   int attrib = recAttr->getColumn()->getAttrId();
387   Uint32 valLen = recAttr->get_size_in_bytes();
388   if (!recAttr->isNULL())
389     valPtr= (const char*) recAttr->aRef();
390 
391   return verifyColValue(record,
392                         attrib,
393                         updates,
394                         valPtr,
395                         valLen);
396 }
397 
398 int
verifyColValue(int record,int attrib,int updates,const char * valPtr,Uint32 valLen)399 HugoCalculator::verifyColValue(int record,
400                                int attrib,
401                                int updates,
402                                const char* valPtr,
403                                Uint32 valLen)
404 {
405   int result = 0;
406 
407   if (attrib == m_updatesCol)
408   {
409     int val= *((const int*) valPtr);
410     if (val != updates)
411     {
412       g_err << "|- Updates column (" << attrib << ")" << endl;
413       g_err << "|- Expected " << updates << " but found " << val << endl;
414       result = -1;
415     }
416   }
417   else if (attrib == m_idCol)
418   {
419     int val= *((const int*) valPtr);
420     if (val != record)
421     {
422       g_err << "|- Identity column (" << attrib << ")" << endl;
423       g_err << "|- Expected " << record << " but found " << val << endl;
424       result = -1;
425     }
426   }
427   else
428   {
429     /* 'Normal' data column */
430     const NdbDictionary::Column* attr = m_tab.getColumn(attrib);
431     Uint32 len = attr->getSizeInBytes(), real_len;
432     char buf[NDB_MAX_TUPLE_SIZE];
433     const char* res = calcValue(record, attrib, updates, buf, len, &real_len);
434     if (res == NULL){
435       if (valPtr != NULL){
436         g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl;
437         g_err << "|- Column length is " << valLen << " bytes" << endl;
438         g_err << "|- Column data follows :" << endl;
439         for (Uint32 j = 0; j < valLen; j ++)
440         {
441           g_err << j << ":" << hex << (Uint32)(Uint8)valPtr[j] << endl;
442         }
443         result = -1;
444       }
445     } else{
446       if (real_len != valLen)
447       {
448         g_err << "|- Invalid data found in attribute " << attrib << ": \""
449               << "Length of expected=" << real_len << endl
450               << "Length of passed="
451               << valLen << endl;
452         result= -1;
453       }
454       else if (memcmp(res, valPtr, real_len) != 0)
455       {
456         g_err << "|- Expected data mismatch on column "
457               << attr->getName() << " length " << real_len
458               << " bytes " << endl;
459         g_err << "|- Bytewise comparison follows :" << endl;
460         for (Uint32 j = 0; j < real_len; j++)
461         {
462           g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)valPtr[j] << "]";
463           if (buf[j] != valPtr[j])
464           {
465             g_err << "==>Match failed!";
466           }
467           g_err << endl;
468         }
469         g_err << endl;
470         result = -1;
471       }
472     }
473   }
474 
475   return result;
476 }
477 
478 int
getIdValue(NDBT_ResultRow * const pRow) const479 HugoCalculator::getIdValue(NDBT_ResultRow* const pRow) const {
480   return pRow->attributeStore(m_idCol)->u_32_value();
481 }
482 
483 int
getUpdatesValue(NDBT_ResultRow * const pRow) const484 HugoCalculator::getUpdatesValue(NDBT_ResultRow* const pRow) const {
485   return pRow->attributeStore(m_updatesCol)->u_32_value();
486 }
487 
488 int
equalForRow(Uint8 * pRow,const NdbRecord * pRecord,int rowId)489 HugoCalculator::equalForRow(Uint8 * pRow,
490                             const NdbRecord* pRecord,
491                             int rowId)
492 {
493   for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
494   {
495     const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
496 
497     if (attr->getPrimaryKey() == true)
498     {
499       char buf[8000];
500       int len = attr->getSizeInBytes();
501       memset(buf, 0, sizeof(buf));
502       Uint32 real_len;
503       const char * value = calcValue(rowId, attrId, 0, buf,
504                                      len, &real_len);
505       require(value != 0); // NULLable PK not supported...
506       Uint32 off = 0;
507       bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
508       if (!ret)
509         abort();
510       memcpy(pRow + off, buf, real_len);
511     }
512   }
513   return NDBT_OK;
514 }
515 
516 int
setValues(Uint8 * pRow,const NdbRecord * pRecord,int rowId,int updateVal)517 HugoCalculator::setValues(Uint8 * pRow,
518                           const NdbRecord* pRecord,
519                           int rowId,
520                           int updateVal)
521 {
522   int res = equalForRow(pRow, pRecord, rowId);
523   if (res != 0)
524   {
525     return res;
526   }
527 
528   for(int attrId = 0; attrId < m_tab.getNoOfColumns(); attrId++)
529   {
530     const NdbDictionary::Column* attr = m_tab.getColumn(attrId);
531 
532     if (attr->getPrimaryKey() == false)
533     {
534       char buf[8000];
535       int len = attr->getSizeInBytes();
536       memset(buf, 0, sizeof(buf));
537       Uint32 real_len;
538       const char * value = calcValue(rowId, attrId, updateVal, buf,
539                                      len, &real_len);
540       if (value != 0)
541       {
542         Uint32 off = 0;
543         bool ret = NdbDictionary::getOffset(pRecord, attrId, off);
544         if (!ret)
545           abort();
546         memcpy(pRow + off, buf, real_len);
547         if (attr->getNullable())
548           NdbDictionary::setNull(pRecord, (char*)pRow, attrId, false);
549       }
550       else
551       {
552         require(attr->getNullable());
553         NdbDictionary::setNull(pRecord, (char*)pRow, attrId, true);
554       }
555     }
556   }
557 
558   return NDBT_OK;
559 }
560