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(©, ((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 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