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