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(©, ((Uint32*)buf)+tmp, 4);
231 copy &= ((1 << size) - 1);
232 memcpy(((Uint32*)buf)+tmp, ©, 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