1 /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* Test av isam-databas: stor test */
24 
25 #include "my_config.h"
26 
27 #include <signal.h>
28 #include <sys/types.h>
29 
30 #include <algorithm>
31 
32 #include "my_compiler.h"
33 #include "my_inttypes.h"
34 #include "my_macros.h"
35 #include "storage/heap/heapdef.h" /* Because of hp_find_block */
36 
37 #define MAX_RECORDS 100000U
38 #define MAX_KEYS 4
39 
40 static int get_options(int argc, char *argv[]);
41 static int rnd(int max_value);
42 static void endprog(int sig_number) MY_ATTRIBUTE((noreturn));
43 
44 static uint flag = 0, verbose = 0, testflag = 0, recant = 10000, silent = 0;
45 static uint keys = MAX_KEYS;
46 static uint16 key1[1001];
47 static bool key3[MAX_RECORDS];
48 static int reclength = 39;
49 
50 static int calc_check(uchar *buf, uint length);
51 static void make_record(uchar *record, uint n1, uint n2, uint n3,
52                         const char *mark, uint count);
53 
54 /* Main program */
55 
main(int argc,char * argv[])56 int main(int argc, char *argv[]) {
57   uint i, j;
58   uint ant, n1, n2, n3;
59   uint write_count, update, opt_delete, check2, dupp_keys, found_key;
60   int error;
61   ulong pos;
62   unsigned long key_check;
63   uchar record[128], record2[128], record3[128], key[10];
64   const char *filename, *filename2;
65   HP_INFO *file, *file2;
66   HP_SHARE *tmp_share;
67   HP_KEYDEF keyinfo[MAX_KEYS];
68   HA_KEYSEG keyseg[MAX_KEYS * 5];
69   HP_HEAP_POSITION position;
70   HP_CREATE_INFO hp_create_info;
71   CHARSET_INFO *cs = &my_charset_latin1;
72   bool unused;
73   MY_INIT(argv[0]); /* init my_sys library & pthreads */
74 
75   filename = "test2";
76   filename2 = "test2_2";
77   file = file2 = nullptr;
78   get_options(argc, argv);
79 
80   memset(&hp_create_info, 0, sizeof(hp_create_info));
81   hp_create_info.max_table_size = 1024L * 1024L * 2;
82   hp_create_info.keys = keys;
83   hp_create_info.keydef = keyinfo;
84   hp_create_info.reclength = reclength;
85   hp_create_info.max_records = (ulong)flag * 100000L;
86   hp_create_info.min_records = (ulong)recant / 2;
87 
88   write_count = update = opt_delete = 0;
89   key_check = 0;
90 
91   keyinfo[0].seg = keyseg;
92   keyinfo[0].keysegs = 1;
93   keyinfo[0].flag = 0;
94   keyinfo[0].algorithm = HA_KEY_ALG_HASH;
95   keyinfo[0].seg[0].type = HA_KEYTYPE_BINARY;
96   keyinfo[0].seg[0].start = 0;
97   keyinfo[0].seg[0].length = 6;
98   keyinfo[0].seg[0].null_bit = 0;
99   keyinfo[0].seg[0].charset = cs;
100   keyinfo[1].seg = keyseg + 1;
101   keyinfo[1].keysegs = 2;
102   keyinfo[1].flag = 0;
103   keyinfo[1].algorithm = HA_KEY_ALG_HASH;
104   keyinfo[1].seg[0].type = HA_KEYTYPE_BINARY;
105   keyinfo[1].seg[0].start = 7;
106   keyinfo[1].seg[0].length = 6;
107   keyinfo[1].seg[0].null_bit = 0;
108   keyinfo[1].seg[0].charset = cs;
109   keyinfo[1].seg[1].type = HA_KEYTYPE_TEXT;
110   keyinfo[1].seg[1].start = 0; /* key in two parts */
111   keyinfo[1].seg[1].length = 6;
112   keyinfo[1].seg[1].null_bit = 0;
113   keyinfo[1].seg[1].charset = cs;
114   keyinfo[2].seg = keyseg + 3;
115   keyinfo[2].keysegs = 1;
116   keyinfo[2].flag = HA_NOSAME;
117   keyinfo[2].algorithm = HA_KEY_ALG_HASH;
118   keyinfo[2].seg[0].type = HA_KEYTYPE_BINARY;
119   keyinfo[2].seg[0].start = 12;
120   keyinfo[2].seg[0].length = 8;
121   keyinfo[2].seg[0].null_bit = 0;
122   keyinfo[2].seg[0].charset = cs;
123   keyinfo[3].seg = keyseg + 4;
124   keyinfo[3].keysegs = 1;
125   keyinfo[3].flag = HA_NOSAME;
126   keyinfo[3].algorithm = HA_KEY_ALG_HASH;
127   keyinfo[3].seg[0].type = HA_KEYTYPE_BINARY;
128   keyinfo[3].seg[0].start = 37;
129   keyinfo[3].seg[0].length = 1;
130   keyinfo[3].seg[0].null_bit = 1;
131   keyinfo[3].seg[0].null_pos = 38;
132   keyinfo[3].seg[0].charset = cs;
133 
134   memset(key1, 0, sizeof(key1));
135   memset(key3, 0, sizeof(key3));
136 
137   printf("- Creating heap-file\n");
138   if (heap_create(filename, &hp_create_info, &tmp_share, &unused) ||
139       !(file = heap_open(filename, 2)))
140     goto err;
141   signal(SIGINT, endprog);
142 
143   printf("- Writing records:s\n");
144   my_stpcpy((char *)record, "          ..... key");
145 
146   for (i = 0; i < recant; i++) {
147     n1 = rnd(1000);
148     n2 = rnd(100);
149     n3 = rnd(std::min(recant * 5, MAX_RECORDS));
150     make_record(record, n1, n2, n3, "Pos", write_count);
151 
152     if (heap_write(file, record)) {
153       if (my_errno() != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0) {
154         printf("Error: %d in write at record: %d\n", my_errno(), i);
155         goto err;
156       }
157       if (verbose) printf("   Double key: %d\n", n3);
158     } else {
159       if (key3[n3] == 1) {
160         printf("Error: Didn't get error when writing second key: '%8d'\n", n3);
161         goto err;
162       }
163       write_count++;
164       key1[n1]++;
165       key3[n3] = true;
166       key_check += n1;
167     }
168     if (testflag == 1 && heap_check_heap(file, false)) {
169       puts("Heap keys crashed");
170       goto err;
171     }
172   }
173   if (testflag == 1) goto end;
174   if (heap_check_heap(file, false)) {
175     puts("Heap keys crashed");
176     goto err;
177   }
178 
179   printf("- Delete\n");
180   for (i = 0; i < write_count / 10; i++) {
181     for (j = rnd(1000) + 1; j > 0 && key1[j] == 0; j--)
182       ;
183     if (j != 0) {
184       sprintf((char *)key, "%6d", j);
185       if (heap_rkey(file, record, 0, key, 6, HA_READ_KEY_EXACT)) {
186         printf("can't find key1: \"%s\"\n", (char *)key);
187         goto err;
188       }
189       if (heap_delete(file, record)) {
190         printf("error: %d; can't delete record: \"%s\"\n", my_errno(),
191                (char *)record);
192         goto err;
193       }
194       opt_delete++;
195       key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
196       key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
197       key_check -= atoi((char *)record);
198       if (testflag == 2 && heap_check_heap(file, false)) {
199         puts("Heap keys crashed");
200         goto err;
201       }
202     } else
203       puts("Warning: Skipping delete test because no dupplicate keys");
204   }
205   if (testflag == 2) goto end;
206   if (heap_check_heap(file, false)) {
207     puts("Heap keys crashed");
208     goto err;
209   }
210 
211   printf("- Update\n");
212   for (i = 0; i < write_count / 10; i++) {
213     n1 = rnd(1000);
214     n2 = rnd(100);
215     n3 = rnd(std::min(recant * 2, MAX_RECORDS));
216     make_record(record2, n1, n2, n3, "XXX", update);
217     if (rnd(2) == 1) {
218       if (heap_scan_init(file)) goto err;
219       j = rnd(write_count - opt_delete);
220       while ((error = heap_scan(file, record) == HA_ERR_RECORD_DELETED) ||
221              (!error && j)) {
222         if (!error) j--;
223       }
224       if (error) goto err;
225     } else {
226       for (j = rnd(1000) + 1; j > 0 && key1[j] == 0; j--)
227         ;
228       if (!key1[j]) continue;
229       sprintf((char *)key, "%6d", j);
230       if (heap_rkey(file, record, 0, key, 6, HA_READ_KEY_EXACT)) {
231         printf("can't find key1: \"%s\"\n", (char *)key);
232         goto err;
233       }
234     }
235     if (heap_update(file, record, record2)) {
236       if (my_errno() != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0) {
237         printf("error: %d; can't update:\nFrom: \"%s\"\nTo:   \"%s\"\n",
238                my_errno(), (char *)record, (char *)record2);
239         goto err;
240       }
241       if (verbose)
242         printf("Double key when tried to update:\nFrom: \"%s\"\nTo:   \"%s\"\n",
243                (char *)record, (char *)record2);
244     } else {
245       key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
246       key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
247       key1[n1]++;
248       key3[n3] = true;
249       update++;
250       key_check = key_check - atoi((char *)record) + n1;
251     }
252     if (testflag == 3 && heap_check_heap(file, false)) {
253       puts("Heap keys crashed");
254       goto err;
255     }
256   }
257   if (testflag == 3) goto end;
258   if (heap_check_heap(file, false)) {
259     puts("Heap keys crashed");
260     goto err;
261   }
262 
263   for (i = 999, dupp_keys = found_key = 0; i > 0; i--) {
264     if (key1[i] > dupp_keys) {
265       dupp_keys = key1[i];
266       found_key = i;
267     }
268     sprintf((char *)key, "%6d", found_key);
269   }
270 
271   if (dupp_keys > 3) {
272     if (!silent) printf("- Read first key - next - delete - next -> last\n");
273     DBUG_PRINT("progpos", ("first - next - delete - next -> last"));
274 
275     if (heap_rkey(file, record, 0, key, 6, HA_READ_KEY_EXACT)) goto err;
276     if (heap_rnext(file, record3)) goto err;
277     if (heap_delete(file, record3)) goto err;
278     key_check -= atoi((char *)record3);
279     key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
280     key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
281     opt_delete++;
282     ant = 2;
283     while ((error = heap_rnext(file, record3)) == 0 ||
284            error == HA_ERR_RECORD_DELETED)
285       if (!error) ant++;
286     if (ant != dupp_keys) {
287       printf("next: I can only find: %d records of %d\n", ant, dupp_keys);
288       goto end;
289     }
290     dupp_keys--;
291     if (heap_check_heap(file, false)) {
292       puts("Heap keys crashed");
293       goto err;
294     }
295 
296     if (!silent)
297       printf(
298           "- Read last key - delete - prev - prev - opt_delete - prev -> "
299           "first\n");
300 
301     if (heap_rlast(file, record3, 0)) goto err;
302     if (heap_delete(file, record3)) goto err;
303     key_check -= atoi((char *)record3);
304     key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
305     key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
306     opt_delete++;
307     if (heap_rprev(file, record3) || heap_rprev(file, record3)) goto err;
308     if (heap_delete(file, record3)) goto err;
309     key_check -= atoi((char *)record3);
310     key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
311     key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
312     opt_delete++;
313     ant = 3;
314     while ((error = heap_rprev(file, record3)) == 0 ||
315            error == HA_ERR_RECORD_DELETED) {
316       if (!error) ant++;
317     }
318     if (ant != dupp_keys) {
319       printf("next: I can only find: %d records of %d\n", ant, dupp_keys);
320       goto end;
321     }
322     dupp_keys -= 2;
323     if (heap_check_heap(file, false)) {
324       puts("Heap keys crashed");
325       goto err;
326     }
327   } else
328     puts("Warning: Not enough duplicated keys:  Skipping delete key check");
329 
330   if (!silent) printf("- Read (first) - next - delete - next -> last\n");
331   DBUG_PRINT("progpos", ("first - next - delete - next -> last"));
332 
333   if (heap_scan_init(file)) goto err;
334   while ((error = heap_scan(file, record3) == HA_ERR_RECORD_DELETED))
335     ;
336   if (error) goto err;
337   if (heap_delete(file, record3)) goto err;
338   key_check -= atoi((char *)record3);
339   opt_delete++;
340   key1[atoi((char *)record + keyinfo[0].seg[0].start)]--;
341   key3[atoi((char *)record + keyinfo[2].seg[0].start)] = false;
342   ant = 0;
343   while ((error = heap_scan(file, record3)) == 0 ||
344          error == HA_ERR_RECORD_DELETED)
345     if (!error) ant++;
346   if (ant != write_count - opt_delete) {
347     printf("next: Found: %d records of %d\n", ant, write_count - opt_delete);
348     goto end;
349   }
350   if (heap_check_heap(file, false)) {
351     puts("Heap keys crashed");
352     goto err;
353   }
354 
355   puts("- Test if: Read rrnd - same - rkey - same");
356   DBUG_PRINT("progpos", ("Read rrnd - same"));
357   pos = rnd(write_count - opt_delete - 5) + 5;
358   heap_scan_init(file);
359   i = 5;
360   while ((error = heap_scan(file, record)) == HA_ERR_RECORD_DELETED ||
361          (error == 0 && pos)) {
362     if (!error) pos--;
363     if (!error && (i-- == 0)) {
364       memmove(record3, record, reclength);
365       heap_position(file, &position);
366     }
367   }
368   if (error) goto err;
369   memmove(record2, record, reclength);
370   if (heap_rsame(file, record, -1) || heap_rsame(file, record2, 2)) goto err;
371   if (memcmp(record2, record, reclength)) {
372     puts("heap_rsame didn't find right record");
373     goto end;
374   }
375 
376   puts("- Test of read through position");
377   if (heap_rrnd(file, record, &position)) goto err;
378   if (memcmp(record3, record, reclength)) {
379     puts("heap_frnd didn't find right record");
380     goto end;
381   }
382 
383   printf("- heap_info\n");
384   {
385     HEAPINFO info;
386     heap_info(file, &info, 0);
387     /* We have to test with opt_delete +1 as this may be the case if the last
388        inserted row was a duplicate key */
389     if (info.records != write_count - opt_delete ||
390         (info.deleted != opt_delete && info.deleted != opt_delete + 1)) {
391       puts("Wrong info from heap_info");
392       printf("Got: records: %ld(%d)  deleted: %ld(%d)\n", info.records,
393              write_count - opt_delete, info.deleted, opt_delete);
394     }
395   }
396 
397   printf("- Read through all records with scan\n");
398   ant = check2 = 0;
399   heap_scan_init(file);
400   while ((error = heap_scan(file, record)) != HA_ERR_END_OF_FILE &&
401          ant < write_count + 10) {
402     if (!error) {
403       ant++;
404       check2 += calc_check(record, reclength);
405     }
406   }
407   if (ant != write_count - opt_delete) {
408     printf("scan: I can only find: %d records of %d\n", ant,
409            write_count - opt_delete);
410     goto end;
411   }
412 
413   for (i = 999, dupp_keys = found_key = 0; i > 0; i--) {
414     if (key1[i] > dupp_keys) {
415       dupp_keys = key1[i];
416       found_key = i;
417     }
418     sprintf((char *)key, "%6d", found_key);
419   }
420   printf("- Read through all keys with first-next-last-prev\n");
421   ant = 0;
422   for (error = heap_rkey(file, record, 0, key, 6, HA_READ_KEY_EXACT); !error;
423        error = heap_rnext(file, record))
424     ant++;
425   if (ant != dupp_keys) {
426     printf("first-next: I can only find: %d records of %d\n", ant, dupp_keys);
427     goto end;
428   }
429 
430   ant = 0;
431   for (error = heap_rlast(file, record, 0); !error;
432        error = heap_rprev(file, record)) {
433     ant++;
434     check2 += calc_check(record, reclength);
435   }
436   if (ant != dupp_keys) {
437     printf("last-prev: I can only find: %d records of %d\n", ant, dupp_keys);
438     goto end;
439   }
440 
441   if (testflag == 4) goto end;
442 
443   printf("- Reading through all rows through keys\n");
444   if (!(file2 = heap_open(filename, 2))) goto err;
445   if (heap_scan_init(file)) goto err;
446   while ((error = heap_scan(file, record)) != HA_ERR_END_OF_FILE) {
447     if (error == 0) {
448       if (heap_rkey(file2, record2, 2, record + keyinfo[2].seg[0].start, 8,
449                     HA_READ_KEY_EXACT)) {
450         printf("can't find key3: \"%.8s\"\n", record + keyinfo[2].seg[0].start);
451         goto err;
452       }
453     }
454   }
455   heap_close(file2);
456 
457   printf("- Creating output heap-file 2\n");
458   hp_create_info.keys = 1;
459   hp_create_info.max_records = 0;
460   hp_create_info.min_records = 0;
461   if (heap_create(filename2, &hp_create_info, &tmp_share, &unused) ||
462       !(file2 = heap_open_from_share_and_register(tmp_share, 2)))
463     goto err;
464 
465   printf("- Copying and removing records\n");
466   if (heap_scan_init(file)) goto err;
467   while ((error = heap_scan(file, record)) != HA_ERR_END_OF_FILE) {
468     if (error == 0) {
469       if (heap_write(file2, record)) goto err;
470       key_check -= atoi((char *)record);
471       write_count++;
472       if (heap_delete(file, record)) goto err;
473       opt_delete++;
474     }
475     pos++;
476   }
477   printf("- Checking heap tables\n");
478   if (heap_check_heap(file, true) || heap_check_heap(file2, true)) {
479     puts("Heap keys crashed");
480     goto err;
481   }
482 
483   if (my_errno() != HA_ERR_END_OF_FILE)
484     printf("error: %d from heap_rrnd\n", my_errno());
485   if (key_check)
486     printf("error: Some read got wrong: check is %ld\n", (long)key_check);
487 
488 end:
489   printf("\nFollowing test have been made:\n");
490   printf("Write records: %d\nUpdate records: %d\nDelete records: %d\n",
491          write_count, update, opt_delete);
492   heap_clear(file);
493   heap_clear(file2);
494   if (heap_close(file) || (file2 && heap_close(file2))) goto err;
495   heap_delete_table(filename2);
496   hp_panic(HA_PANIC_CLOSE);
497   my_end(MY_GIVE_INFO);
498   return (0);
499 err:
500   printf("Got error: %d when using heap-database\n", my_errno());
501   (void)heap_close(file);
502   return (1);
503 } /* main */
504 
505 /* Read options */
506 
get_options(int argc,char * argv[])507 static int get_options(int argc, char *argv[]) {
508   char *pos, *progname;
509 
510   progname = argv[0];
511 
512   while (--argc > 0 && *(pos = *(++argv)) == '-') {
513     switch (*++pos) {
514       case 'B': /* Big file */
515         flag = 1;
516         break;
517       case 'v': /* verbose */
518         verbose = 1;
519         break;
520       case 'm': /* records */
521         recant = atoi(++pos);
522         break;
523       case 's':
524         silent = 1;
525         break;
526       case 't':
527         testflag = atoi(++pos); /* testmod */
528         break;
529       case 'V':
530       case 'I':
531       case '?':
532         printf("%s  Ver 1.1 for %s at %s\n", progname, SYSTEM_TYPE,
533                MACHINE_TYPE);
534         puts("TCX Datakonsult AB, by Monty, for your professional use\n");
535         printf("Usage: %s [-?ABIKLsWv] [-m#] [-t#]\n", progname);
536         exit(0);
537       case '#':
538         DBUG_PUSH(++pos);
539         break;
540     }
541   }
542   return 0;
543 } /* get options */
544 
545 /* Generate a random value in intervall 0 <=x <= n */
546 
rnd(int max_value)547 static int rnd(int max_value) {
548   return (int)((rand() & 32767) / 32767.0 * max_value);
549 } /* rnd */
550 
endprog(int sig_number MY_ATTRIBUTE ((unused)))551 static void endprog(int sig_number MY_ATTRIBUTE((unused))) {
552   {
553     hp_panic(HA_PANIC_CLOSE);
554     my_end(1);
555     exit(1);
556   }
557 }
558 
calc_check(uchar * buf,uint length)559 static int calc_check(uchar *buf, uint length) {
560   int check = 0;
561   while (length--) check += (int)(uchar) * (buf++);
562   return check;
563 }
564 
make_record(uchar * record,uint n1,uint n2,uint n3,const char * mark,uint count)565 static void make_record(uchar *record, uint n1, uint n2, uint n3,
566                         const char *mark, uint count) {
567   memset(record, ' ', reclength);
568   sprintf((char *)record, "%6d:%4d:%8d:%3.3s: %4d", n1, n2, n3, mark, count);
569   record[37] = 'A'; /* Store A in null key */
570   record[38] = 1;   /* set as null */
571 }
572