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 /*
26 * testDataBuffers
27 *
28 * Test getValue() of byte arrays:
29 * - using application buffers of different alignments and sizes
30 * - using NdbApi allocated small (<32) and big (>=32) buffers
31 *
32 * Verifies fixes to tickets 189 and 206.
33 *
34 * Options: see printusage() below.
35 *
36 * Creates tables TB00 to TB15
37 */
38
39 #include <ndb_global.h>
40
41 #include <NdbMain.h>
42 #include <NdbOut.hpp>
43 #include <NdbApi.hpp>
44 #include <NdbTest.hpp>
45 #include <NdbSchemaCon.hpp>
46 // limits
47 static int const MaxAttr = 64;
48 static int const MaxOper = 1000;
49 static int const MaxSize = 10000;
50 static int const MaxOff = 64; // max offset to add to data buffer
51 static int const MaxData = MaxSize + MaxOff + 100;
52
53 // options
54 static int attrcnt = 25;
55 static int existok = 0;
56 static bool kontinue = false;
57 static int loopcnt = 1;
58 static int opercnt = 100; // also does this many scans
59 static int randomizer = 171317;
60 static int sizelim = 500;
61 static int xverbose = 0;
62
printusage()63 static void printusage() {
64 ndbout
65 << "usage: testDataBuffers options [default/max]"
66 << endl
67 << "NOTE: too large combinations result in NDB error"
68 << endl
69 << "-a N number of attributes (including the key) [25/64]"
70 << endl
71 << "-e no error if table exists (assumed to have same structure)"
72 << endl
73 << "-k on error continue with next test case"
74 << endl
75 << "-l N number of loops to run, 0 means infinite [1]"
76 << endl
77 << "-o N number of operations (rows in each table) [100/1000]"
78 << endl
79 << "-r N source of randomness (big number (prime)) [171317]"
80 << endl
81 << "-s N array size limit (rounded up in some tests) [500/10000]"
82 << endl
83 << "-x extremely verbose"
84 << endl
85 << "Tables: TB00 .. TB15"
86 << endl
87 ;
88 }
89
90 static Ndb* ndb = 0;
91 static NdbSchemaCon* tcon = 0;
92 static NdbSchemaOp* top = 0;
93 static NdbConnection* con = 0;
94 static NdbOperation* op = 0;
95 static NdbScanOperation* sop = 0;
96
97 static int
ndberror(char const * fmt,...)98 ndberror(char const* fmt, ...)
99 {
100 va_list ap;
101 char buf[200];
102 va_start(ap, fmt);
103 BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
104 va_end(ap);
105 ndbout << buf << " --" << endl;
106 if (ndb)
107 ndbout << "ndb : " << ndb->getNdbError() << endl;
108 if (tcon)
109 ndbout << "tcon: " << tcon->getNdbError() << endl;
110 if (top)
111 ndbout << "top: " << top->getNdbError() << endl;
112 if (con)
113 ndbout << "con : " << con->getNdbError() << endl;
114 if (op)
115 ndbout << "op : " << op->getNdbError() << endl;
116 return -1;
117 }
118
119 static int
chkerror(char const * fmt,...)120 chkerror(char const* fmt, ...)
121 {
122 va_list ap;
123 char buf[200];
124 va_start(ap, fmt);
125 BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
126 va_end(ap);
127 ndbout << "*** check failed: " << buf << " ***" << endl;
128 return -1;
129 }
130
131 // alignment of addresses and data sizes
132
isAligned(UintPtr x)133 static bool isAligned(UintPtr x)
134 {
135 return ((x & 3) == 0);
136 }
isAligned(char * p)137 static bool isAligned(char* p)
138 {
139 return isAligned(UintPtr(p));
140 }
toAligned(unsigned x)141 static unsigned toAligned(unsigned x)
142 {
143 while (! isAligned(x))
144 x++;
145 return x;
146 }
toAligned(char * p)147 static char* toAligned(char* p)
148 {
149 while (! isAligned(p))
150 p++;
151 return p;
152 }
153
154 // byte value for key k column i byte j
byteVal(int k,int i,int j)155 static int byteVal(int k, int i, int j)
156 {
157 return '0' + (k + i + j) % 10;
158 }
159
160 // tables
161
162 static char tab[20] = "";
163
164 static struct col {
165 char aAttrName[20];
166 AttrType aAttrType;
167 int aAttrSize;
168 int aArraySize;
169 KeyType aTupleKey;
170 bool nullable;
171 NdbRecAttr* aRa;
172 char* buf;
173 int bufsiz;
174 char data[MaxData];
175 } ccol[MaxAttr];
176
177 static int key = 0;
178
179 // independent test bits
180 static bool alignAddr; // align our buffer addresses to 4x
181 static bool alignSize; // align data sizes to 4x
182 static bool useBuf; // use our buffers for output
183 static bool noRandom; // do not randomize sizes and offsets
184 static int testbits = 4;
185
186 static int
makeSize(int i)187 makeSize(int i)
188 {
189 int n;
190 if (noRandom)
191 n = i;
192 else
193 n = i * randomizer;
194 n %= sizelim;
195 if (n <= 0)
196 n = 1;
197 if (alignSize)
198 n = toAligned(n);
199 return n;
200 }
201
202 static int
makeOff(int k)203 makeOff(int k)
204 {
205 int n;
206 if (alignAddr)
207 n = 0;
208 else if (noRandom)
209 n = k;
210 else
211 n = k * randomizer;
212 n %= MaxOff;
213 if (n < 0)
214 n = -n;
215 return n;
216 }
217
218 static int
testcase(Ndb_cluster_connection & cc,int flag)219 testcase(Ndb_cluster_connection&cc, int flag)
220 {
221 ndbout << "--- case " << flag << " ---" << endl;
222 sprintf(tab, "TB%02d", flag);
223
224 alignAddr = ! (flag & 1);
225 ndbout << (alignAddr ? "align addresses" : "mis-align addresses") << endl;
226 alignSize = ! (flag & 2);
227 ndbout << (alignSize ? "align data sizes" : "mis-align data sizes") << endl;
228 useBuf = ! (flag & 4);
229 ndbout << (useBuf ? "use our buffers" : "use ndbapi buffers") << endl;
230 noRandom = ! (flag & 8);
231 ndbout << (noRandom ? "simple sizes" : "randomize sizes") << endl;
232
233 int smax = 0, stot = 0, i;
234 if (xverbose)
235 ndbout << "- define table " << tab << endl;
236 for (i = 0; i < attrcnt; i++) {
237 col& c = ccol[i];
238 memset(&c, 0, sizeof(c));
239 sprintf(c.aAttrName, "C%d", i);
240 if (i == 0) {
241 c.aAttrType = UnSigned;
242 c.aAttrSize = 32;
243 c.aArraySize = 1;
244 c.aTupleKey = TupleKey;
245 c.nullable = false;
246 } else {
247 c.aAttrType = String;
248 c.aAttrSize = 8;
249 c.aArraySize = makeSize(i);
250 if (smax < c.aArraySize)
251 smax = c.aArraySize;
252 stot += c.aArraySize;
253 c.aTupleKey = NoKey;
254 c.nullable = true;
255 if (xverbose)
256 ndbout << "-- column " << i << " size=" << c.aArraySize << endl;
257 }
258 c.buf = toAligned(c.data);
259 c.bufsiz = (int)(sizeof(c.data) - (c.buf - c.data));
260 }
261 ndbout << "tab=" << tab << " cols=" << attrcnt
262 << " size max=" << smax << " tot=" << stot << endl;
263
264 if ((tcon = NdbSchemaCon::startSchemaTrans(ndb)) == 0)
265 return ndberror("startSchemaTransaction");
266 if ((top = tcon->getNdbSchemaOp()) == 0)
267 return ndberror("getNdbSchemaOp");
268 if (top->createTable(tab) < 0)
269 return ndberror("createTable");
270 for (i = 0; i < attrcnt; i++) {
271 col& c = ccol[i];
272 if (top->createAttribute(
273 c.aAttrName,
274 c.aTupleKey,
275 c.aAttrSize,
276 c.aArraySize,
277 c.aAttrType,
278 MMBased,
279 c.nullable
280 ) < 0)
281 return ndberror("createAttribute col=%d", i);
282 }
283 if (tcon->execute() < 0) {
284 if (! (tcon->getNdbError().code == 721 && existok))
285 return ndberror("execute");
286 ndbout << "using " << tab << endl;
287 } else {
288 ndbout << "created " << tab << endl;
289 }
290 top = 0;
291 tcon = 0;
292
293 if (xverbose)
294 ndbout << "- delete" << endl;
295 int delcnt = 0;
296 for (key = 0; key < opercnt; key++) {
297 if ((con = ndb->startTransaction()) == 0)
298 return ndberror("startTransaction key=%d", key);
299 if ((op = con->getNdbOperation(tab)) == 0)
300 return ndberror("getNdbOperation key=%d", key);
301 if (op->deleteTuple() < 0)
302 return ndberror("deleteTuple key=%d", key);
303 for (i = 0; i < attrcnt; i++) {
304 col& c = ccol[i];
305 if (i == 0) {
306 if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
307 return ndberror("equal key=%d", key);
308 } else {
309 }
310 }
311 if (con->execute(Commit) < 0) {
312 if (con->getNdbError().code != 626)
313 return ndberror("execute key=%d", key);
314 } else {
315 delcnt++;
316 }
317 ndb->closeTransaction(con);
318 }
319 con = 0;
320 op = 0;
321 ndbout << "deleted " << delcnt << endl;
322
323 if (xverbose)
324 ndbout << "- insert" << endl;
325 for (key = 0; key < opercnt; key++) {
326 int off = makeOff(key);
327 if ((con = ndb->startTransaction()) == 0)
328 return ndberror("startTransaction key=%d", key);
329 if ((op = con->getNdbOperation(tab)) == 0)
330 return ndberror("getNdbOperation key=%d", key);
331 if (op->insertTuple() < 0)
332 return ndberror("insertTuple key=%d", key);
333 for (i = 0; i < attrcnt; i++) {
334 col& c = ccol[i];
335 if (i == 0) {
336 if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
337 return ndberror("equal key=%d", key);
338 } else {
339 memset(c.buf, 'A', c.bufsiz);
340 for (int j = 0; j < c.aArraySize; j++)
341 c.buf[j + off] = byteVal(key, i, j);
342 if (op->setValue(c.aAttrName, c.buf + off, c.aArraySize) < 0)
343 return ndberror("setValue key=%d col=%d", key, i);
344 }
345 }
346 if (con->execute(Commit) < 0)
347 return ndberror("execute key=%d", key);
348 ndb->closeTransaction(con);
349 }
350 con = 0;
351 op = 0;
352 ndbout << "inserted " << key << endl;
353
354 if (xverbose)
355 ndbout << "- select" << endl;
356 for (key = 0; key < opercnt; key++) {
357 int off = makeOff(key);
358 if (xverbose)
359 ndbout << "-- key " << key << " off=" << off << endl;
360 if ((con = ndb->startTransaction()) == 0)
361 return ndberror("startTransaction key=%d", key);
362 if ((op = con->getNdbOperation(tab)) == 0)
363 return ndberror("getNdbOperation key=%d", key);
364 if (op->readTuple() < 0)
365 return ndberror("readTuple key=%d", key);
366 for (i = 0; i < attrcnt; i++) {
367 col& c = ccol[i];
368 if (i == 0) {
369 if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
370 return ndberror("equal key=%d", key);
371 } else {
372 if (xverbose) {
373 char tmp[20];
374 if (useBuf)
375 sprintf(tmp, "0x%p", c.buf + off);
376 else
377 strcpy(tmp, "ndbapi");
378 ndbout << "--- column " << i << " addr=" << tmp << endl;
379 }
380 memset(c.buf, 'B', c.bufsiz);
381 if (useBuf) {
382 if (op->getValue(c.aAttrName, c.buf + off) == NULL)
383 return ndberror("getValue key=%d col=%d", key, i);
384 } else {
385 if ((c.aRa = op->getValue(c.aAttrName)) == NULL)
386 return ndberror("getValue key=%d col=%d", key, i);
387 }
388 }
389 }
390 if (con->execute(Commit) != 0)
391 return ndberror("execute key=%d", key);
392 for (i = 0; i < attrcnt; i++) {
393 col& c = ccol[i];
394 if (i == 0) {
395 } else if (useBuf) {
396 int j;
397 for (j = 0; j < off; j++) {
398 if (c.buf[j] != 'B') {
399 return chkerror("mismatch before key=%d col=%d pos=%d ok=%02x bad=%02x",
400 key, i, j, 'B', c.buf[j]);
401 }
402 }
403 for (j = 0; j < c.aArraySize; j++) {
404 if (c.buf[j + off] != byteVal(key, i, j)) {
405 return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
406 key, i, j, byteVal(key, i, j), c.buf[j]);
407 }
408 }
409 for (j = c.aArraySize + off; j < c.bufsiz; j++) {
410 if (c.buf[j] != 'B') {
411 return chkerror("mismatch after key=%d col=%d pos=%d ok=%02x bad=%02x",
412 key, i, j, 'B', c.buf[j]);
413 }
414 }
415 } else {
416 char* buf = c.aRa->aRef();
417 if (buf == 0)
418 return ndberror("null aRef key=%d col%d", key, i);
419 for (int j = 0; j < c.aArraySize; j++) {
420 if (buf[j] != byteVal(key, i, j)) {
421 return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
422 key, i, j, byteVal(key, i, j), buf[j]);
423 }
424 }
425 }
426 }
427 ndb->closeTransaction(con);
428 }
429 con = 0;
430 op = 0;
431 ndbout << "selected " << key << endl;
432
433 if (xverbose)
434 ndbout << "- scan" << endl;
435 char found[MaxOper];
436 int k;
437 NdbDictionary::Dictionary * dict = ndb->getDictionary();
438 const NdbDictionary::Table * table = dict->getTable(tab);
439
440 for (k = 0; k < opercnt; k++)
441 found[k] = 0;
442 for (key = 0; key < opercnt; key++) {
443 int off = makeOff(key);
444 NdbInterpretedCode codeObj(table);
445 NdbInterpretedCode *code= &codeObj;
446
447 if (xverbose)
448 ndbout << "-- key " << key << " off=" << off << endl;
449 int newkey = 0;
450 if ((con = ndb->startTransaction()) == 0)
451 return ndberror("startTransaction key=%d", key);
452 if ((op = sop = con->getNdbScanOperation(tab)) == 0)
453 return ndberror("getNdbOperation key=%d", key);
454 if (sop->readTuples(1))
455 return ndberror("openScanRead key=%d", key);
456 {
457 col& c = ccol[0];
458 Uint32 colNum= table->getColumn(c.aAttrName)->getAttrId();
459 if (code->load_const_u32(1, key) < 0)
460 return ndberror("load_const_u32");
461 if (code->read_attr(2, colNum) < 0)
462 return ndberror("read_attr");
463 if (code->branch_eq(1, 2, 0) < 0)
464 return ndberror("branch_eq");
465 if (code->interpret_exit_nok() < 0)
466 return ndberror("interpret_exit_nok");
467 if (code->def_label(0) < 0)
468 return ndberror("def_label");
469 if (code->interpret_exit_ok() < 0)
470 return ndberror("interpret_exit_ok");
471 if (code->finalise() != 0)
472 return ndberror("finalise");
473 if (sop->setInterpretedCode(code) != 0)
474 return ndberror("setInterpretedCode");
475 }
476 for (i = 0; i < attrcnt; i++) {
477 col& c = ccol[i];
478 if (i == 0) {
479 if (op->getValue(c.aAttrName, (char*)&newkey) == NULL)
480 return ndberror("getValue key=%d col=%d", key, i);
481 } else {
482 if (xverbose) {
483 char tmp[20];
484 if (useBuf)
485 sprintf(tmp, "0x%p", c.buf + off);
486 else
487 strcpy(tmp, "ndbapi");
488 ndbout << "--- column " << i << " addr=" << tmp << endl;
489 }
490 memset(c.buf, 'C', c.bufsiz);
491 if (useBuf) {
492 if (op->getValue(c.aAttrName, c.buf + off) == NULL)
493 return ndberror("getValue key=%d col=%d", key, i);
494 } else {
495 if ((c.aRa = op->getValue(c.aAttrName)) == NULL)
496 return ndberror("getValue key=%d col=%d", key, i);
497 }
498 }
499 }
500 if (con->execute(NoCommit) < 0)
501 return ndberror("executeScan key=%d", key);
502 int ret, cnt = 0;
503 while ((ret = sop->nextResult()) == 0) {
504 if (key != newkey)
505 return ndberror("unexpected key=%d newkey=%d", key, newkey);
506 for (i = 1; i < attrcnt; i++) {
507 col& c = ccol[i];
508 if (useBuf) {
509 int j;
510 for (j = 0; j < off; j++) {
511 if (c.buf[j] != 'C') {
512 return chkerror("mismatch before key=%d col=%d pos=%d ok=%02x bad=%02x",
513 key, i, j, 'C', c.buf[j]);
514 }
515 }
516 for (j = 0; j < c.aArraySize; j++) {
517 if (c.buf[j + off] != byteVal(key, i, j)) {
518 return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
519 key, i, j, byteVal(key, i, j), c.buf[j]);
520 }
521 }
522 for (j = c.aArraySize + off; j < c.bufsiz; j++) {
523 if (c.buf[j] != 'C') {
524 return chkerror("mismatch after key=%d col=%d pos=%d ok=%02x bad=%02x",
525 key, i, j, 'C', c.buf[j]);
526 }
527 }
528 } else {
529 char* buf = c.aRa->aRef();
530 if (buf == 0)
531 return ndberror("null aRef key=%d col%d", key, i);
532 for (int j = 0; j < c.aArraySize; j++) {
533 if (buf[j] != byteVal(key, i, j)) {
534 return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
535 key, i, j, byteVal(key, i, j), buf[j]);
536 }
537 }
538 }
539 }
540 cnt++;
541 }
542 if (ret < 0)
543 return ndberror("nextScanResult key=%d", key);
544 if (cnt != 1)
545 return ndberror("scan key=%d found %d", key, cnt);
546 found[key] = 1;
547 ndb->closeTransaction(con);
548 }
549 con = 0;
550 op = 0;
551 for (k = 0; k < opercnt; k++)
552 if (! found[k])
553 return ndberror("key %d not found", k);
554 ndbout << "scanned " << key << endl;
555
556 ndbout << "done" << endl;
557 return 0;
558 }
559
560 NDB_COMMAND(testDataBuffers, "testDataBuffers", "testDataBuffers", "testDataBuffers", 65535)
561 {
562 int i;
563 ndb_init();
564 while (++argv, --argc > 0) {
565 char const* p = argv[0];
566 if (*p++ != '-' || strlen(p) != 1)
567 goto wrongargs;
568 switch (*p) {
569 case 'a':
570 if (++argv, --argc > 0) {
571 attrcnt = atoi(argv[0]);
572 if (1 <= attrcnt && attrcnt <= MaxAttr)
573 break;
574 }
575 goto wrongargs;
576 case 'e':
577 existok = 1;
578 break;
579 case 'k':
580 kontinue = true;
581 break;
582 case 'l':
583 if (++argv, --argc > 0) {
584 loopcnt = atoi(argv[0]);
585 if (0 <= loopcnt)
586 break;
587 }
588 goto wrongargs;
589 case 'o':
590 if (++argv, --argc > 0) {
591 opercnt = atoi(argv[0]);
592 if (0 <= opercnt && opercnt <= MaxOper)
593 break;
594 }
595 goto wrongargs;
596 case 'r':
597 if (++argv, --argc > 0) {
598 randomizer = atoi(argv[0]);
599 if (1 <= randomizer)
600 break;
601 }
602 goto wrongargs;
603 case 's':
604 if (++argv, --argc > 0) {
605 sizelim = atoi(argv[0]);
606 if (1 <= sizelim && sizelim <= MaxSize)
607 break;
608 }
609 goto wrongargs;
610 case 'x':
611 xverbose = 1;
612 break;
613 default:
614 wrongargs:
615 printusage();
616 return NDBT_ProgramExit(NDBT_WRONGARGS);
617 }
618 }
619
620 unsigned ok = true;
621
622 Ndb_cluster_connection con;
623 if(con.connect(12, 5, 1))
624 {
625 return NDBT_ProgramExit(NDBT_FAILED);
626 }
627
628 ndb = new Ndb(&con, "TEST_DB");
629 if (ndb->init() != 0)
630 {
631 ndberror("init");
632 ok = false;
633 goto out;
634 }
635 if (ndb->waitUntilReady(30) < 0)
636 {
637 ndberror("waitUntilReady");
638 ok = false;
639 goto out;
640 }
641
642 for (i = 1; 0 == loopcnt || i <= loopcnt; i++) {
643 ndbout << "=== loop " << i << " ===" << endl;
644 for (int flag = 0; flag < (1<<testbits); flag++) {
645 if (testcase(con, flag) < 0) {
646 ok = false;
647 if (! kontinue)
648 goto out;
649 }
650 NdbDictionary::Dictionary * dict = ndb->getDictionary();
651 dict->dropTable(tab);
652 }
653 }
654
655 out:
656 delete ndb;
657 return NDBT_ProgramExit(ok ? NDBT_OK : NDBT_FAILED);
658 }
659
660 // vim: set sw=4:
661