1 /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2    Copyright (c) 2020, MariaDB Corporation.
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 as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
16 
17 /* Testing of the basic functions of a MARIA table */
18 
19 #include "maria_def.h"
20 #include <my_getopt.h>
21 #include <m_string.h>
22 #include "ma_control_file.h"
23 #include "ma_loghandler.h"
24 #include "ma_checkpoint.h"
25 #include "trnman.h"
26 
27 extern PAGECACHE *maria_log_pagecache;
28 extern const char *maria_data_root;
29 
30 #define MAX_REC_LENGTH 1024
31 
32 static void usage();
33 
34 static int rec_pointer_size=0, flags[50], testflag, checkpoint;
35 static int key_field=FIELD_SKIP_PRESPACE,extra_field=FIELD_SKIP_ENDSPACE;
36 static int key_type=HA_KEYTYPE_NUM;
37 static int create_flag=0;
38 static ulong blob_length;
39 static enum data_file_type record_type= DYNAMIC_RECORD;
40 
41 static uint insert_count, update_count, remove_count;
42 static uint pack_keys=0, pack_seg=0, key_length;
43 static uint unique_key=HA_NOSAME;
44 static uint die_in_middle_of_transaction;
45 static my_bool pagecacheing, null_fields, silent, skip_update, opt_unique;
46 static my_bool verbose, skip_delete, transactional;
47 static my_bool opt_versioning= 0;
48 static MARIA_COLUMNDEF recinfo[4];
49 static MARIA_KEYDEF keyinfo[10];
50 static HA_KEYSEG keyseg[10];
51 static HA_KEYSEG uniqueseg[10];
52 
53 static int run_test(const char *filename);
54 static void get_options(int argc, char *argv[]);
55 static void create_key(uchar *key,uint rownr);
56 static void create_record(uchar *record,uint rownr);
57 static void update_record(uchar *record);
58 
59 /*
60   These are here only for testing of recovery with undo. We are not
61   including maria_def.h here as this test is also to be an example of
62   how to use maria outside of the maria directory
63 */
64 
65 extern int _ma_flush_table_files(MARIA_HA *info, uint flush_data_or_index,
66                                  enum flush_type flush_type_for_data,
67                                  enum flush_type flush_type_for_index);
68 #define MARIA_FLUSH_DATA  1
69 
70 
main(int argc,char * argv[])71 int main(int argc,char *argv[])
72 {
73   char buff[FN_REFLEN];
74 #ifdef SAFE_MUTEX
75   safe_mutex_deadlock_detector= 1;
76 #endif
77   MY_INIT(argv[0]);
78   maria_data_root= ".";
79   get_options(argc,argv);
80   /* Maria requires that we always have a page cache */
81   if (maria_init() ||
82       (init_pagecache(maria_pagecache, maria_block_size * 16, 0, 0,
83                       maria_block_size, 0, MY_WME) == 0) ||
84       ma_control_file_open(TRUE, TRUE, TRUE) ||
85       (init_pagecache(maria_log_pagecache,
86                       TRANSLOG_PAGECACHE_SIZE, 0, 0,
87                       TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0) ||
88       translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
89                     0, 0, maria_log_pagecache,
90                     TRANSLOG_DEFAULT_FLAGS, 0) ||
91       (transactional && (trnman_init(0) || ma_checkpoint_init(0))))
92   {
93     fprintf(stderr, "Error in initialization\n");
94     exit(1);
95   }
96   if (opt_versioning)
97     init_thr_lock();
98 
99   exit(run_test(fn_format(buff, "test1", maria_data_root, "", MYF(0))));
100 }
101 
102 
run_test(const char * filename)103 static int run_test(const char *filename)
104 {
105   MARIA_HA *file;
106   int i,j= 0,error,deleted,rec_length,uniques=0;
107   uint offset_to_key;
108   ha_rows found,row_count;
109   uchar record[MAX_REC_LENGTH],key[MAX_REC_LENGTH],read_record[MAX_REC_LENGTH];
110   MARIA_UNIQUEDEF uniquedef;
111   MARIA_CREATE_INFO create_info;
112 
113   if (die_in_middle_of_transaction)
114     null_fields= 1;
115 
116   bzero((char*) recinfo,sizeof(recinfo));
117   bzero((char*) &create_info,sizeof(create_info));
118 
119   /* First define 2 columns */
120   create_info.null_bytes= 1;
121   recinfo[0].type= key_field;
122   recinfo[0].length= (key_field == FIELD_BLOB ? 4+portable_sizeof_char_ptr :
123 		      key_length);
124   if (key_field == FIELD_VARCHAR)
125     recinfo[0].length+= HA_VARCHAR_PACKLENGTH(key_length);
126   recinfo[1].type=extra_field;
127   recinfo[1].length= (extra_field == FIELD_BLOB ? 4 + portable_sizeof_char_ptr : 24);
128   if (extra_field == FIELD_VARCHAR)
129     recinfo[1].length+= HA_VARCHAR_PACKLENGTH(recinfo[1].length);
130   recinfo[1].null_bit= null_fields ? 2 : 0;
131 
132   if (opt_unique)
133   {
134     recinfo[2].type=FIELD_CHECK;
135     recinfo[2].length=MARIA_UNIQUE_HASH_LENGTH;
136   }
137   rec_length= recinfo[0].length + recinfo[1].length + recinfo[2].length +
138     create_info.null_bytes;
139 
140   if (key_type == HA_KEYTYPE_VARTEXT1 &&
141       key_length > 255)
142     key_type= HA_KEYTYPE_VARTEXT2;
143 
144   /* Define a key over the first column */
145   keyinfo[0].seg=keyseg;
146   keyinfo[0].keysegs=1;
147   keyinfo[0].block_length= 0;                   /* Default block length */
148   keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
149   keyinfo[0].seg[0].type= key_type;
150   keyinfo[0].seg[0].flag= pack_seg;
151   keyinfo[0].seg[0].start=1;
152   keyinfo[0].seg[0].length=key_length;
153   keyinfo[0].seg[0].null_bit= null_fields ? 2 : 0;
154   keyinfo[0].seg[0].null_pos=0;
155   keyinfo[0].seg[0].language= default_charset_info->number;
156   if (pack_seg & HA_BLOB_PART)
157   {
158     keyinfo[0].seg[0].bit_start=4;		/* Length of blob length */
159   }
160   keyinfo[0].flag = (uint8) (pack_keys | unique_key);
161 
162   bzero((uchar*) flags,sizeof(flags));
163   if (opt_unique)
164   {
165     uint start;
166     uniques=1;
167     bzero((char*) &uniquedef,sizeof(uniquedef));
168     bzero((char*) uniqueseg,sizeof(uniqueseg));
169     uniquedef.seg=uniqueseg;
170     uniquedef.keysegs=2;
171 
172     /* Make a unique over all columns (except first NULL fields) */
173     for (i=0, start=1 ; i < 2 ; i++)
174     {
175       uniqueseg[i].start=start;
176       start+=recinfo[i].length;
177       uniqueseg[i].length=recinfo[i].length;
178       uniqueseg[i].language= default_charset_info->number;
179     }
180     uniqueseg[0].type= key_type;
181     uniqueseg[0].null_bit= null_fields ? 2 : 0;
182     uniqueseg[1].type= HA_KEYTYPE_TEXT;
183     if (extra_field == FIELD_BLOB)
184     {
185       uniqueseg[1].length=0;			/* The whole blob */
186       uniqueseg[1].bit_start=4;			/* long blob */
187       uniqueseg[1].flag|= HA_BLOB_PART;
188     }
189     else if (extra_field == FIELD_VARCHAR)
190     {
191       uniqueseg[1].flag|= HA_VAR_LENGTH_PART;
192       uniqueseg[1].type= (HA_VARCHAR_PACKLENGTH(recinfo[1].length-1) == 1 ?
193                           HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2);
194     }
195   }
196   else
197     uniques=0;
198 
199   offset_to_key= MY_TEST(null_fields);
200   if (key_field == FIELD_BLOB || key_field == FIELD_VARCHAR)
201     offset_to_key+= 2;
202 
203   if (!silent)
204     printf("- Creating maria file\n");
205   create_info.max_rows=(ulong) (rec_pointer_size ?
206 				(1L << (rec_pointer_size*8))/40 :
207 				0);
208   create_info.transactional= transactional;
209   if (maria_create(filename, record_type, 1, keyinfo,2+opt_unique,recinfo,
210 		uniques, &uniquedef, &create_info,
211 		create_flag))
212     goto err;
213   if (!(file=maria_open(filename,2,HA_OPEN_ABORT_IF_LOCKED, 0)))
214     goto err;
215   if (!silent)
216     printf("- Writing key:s\n");
217 
218   if (maria_begin(file))
219     goto err;
220   if (opt_versioning)
221     maria_versioning(file, 1);
222   my_errno=0;
223   row_count=deleted=0;
224   for (i=49 ; i>=1 ; i-=2 )
225   {
226     if (insert_count-- == 0)
227     {
228       if (testflag)
229         break;
230       maria_close(file);
231       exit(0);
232     }
233     j=i%25 +1;
234     create_record(record,j);
235     error=maria_write(file,record);
236     if (!error)
237       row_count++;
238     flags[j]=1;
239     if (verbose || error)
240       printf("J= %2d  maria_write: %d  errno: %d\n", j,error,my_errno);
241   }
242 
243   if (maria_commit(file) || maria_begin(file))
244     goto err;
245 
246   if (checkpoint == 1 && ma_checkpoint_execute(CHECKPOINT_MEDIUM, FALSE))
247     goto err;
248 
249   if (testflag == 1)
250     goto end;
251 
252   /* Insert 2 rows with null values */
253   if (null_fields)
254   {
255     create_record(record,0);
256     error=maria_write(file,record);
257     if (!error)
258       row_count++;
259     if (verbose || error)
260       printf("J= NULL  maria_write: %d  errno: %d\n", error,my_errno);
261     error=maria_write(file,record);
262     if (!error)
263       row_count++;
264     if (verbose || error)
265       printf("J= NULL  maria_write: %d  errno: %d\n", error,my_errno);
266     flags[0]=2;
267   }
268 
269   if (checkpoint == 2 && ma_checkpoint_execute(CHECKPOINT_MEDIUM, FALSE))
270     goto err;
271 
272   if (testflag == 2)
273   {
274     printf("Terminating after inserts\n");
275     goto end;
276   }
277 
278   if (maria_commit(file) || maria_begin(file))
279     goto err;
280 
281   if (!skip_update)
282   {
283     if (opt_unique)
284     {
285       if (!silent)
286 	printf("- Checking unique constraint\n");
287       create_record(record,j);                  /* Check last created row */
288       if (!maria_write(file,record) || my_errno != HA_ERR_FOUND_DUPP_UNIQUE)
289       {
290 	printf("unique check failed\n");
291       }
292     }
293     if (!silent)
294       printf("- Updating rows\n");
295 
296     create_key(key, j);
297     if ((maria_rkey(file, read_record, 0, key,
298                            HA_WHOLE_KEY, HA_READ_KEY_EXACT)))
299       printf("Can't find last written row with maria_rkey\n");
300 
301     /* Update first last row to force extend of file */
302     if (maria_rsame(file,read_record,-1))
303     {
304       printf("Can't find last row with maria_rsame\n");
305     }
306     else
307     {
308       memcpy(record,read_record,rec_length);
309       update_record(record);
310       if (maria_update(file,read_record,record))
311       {
312 	printf("Can't update last row: %.*s\n",
313 	       keyinfo[0].seg[0].length,read_record+1);
314       }
315     }
316 
317     /* Read through all rows and update them */
318     maria_scan_init(file);
319 
320     found=0;
321     while ((error= maria_scan(file,read_record)) == 0)
322     {
323       if (--update_count == 0) { maria_close(file); exit(0) ; }
324       memcpy(record,read_record,rec_length);
325       update_record(record);
326       if (maria_update(file,read_record,record))
327       {
328 	printf("Can't update row: %.*s, error: %d\n",
329 	       keyinfo[0].seg[0].length,record+1,my_errno);
330       }
331       found++;
332     }
333     if (found != row_count)
334       printf("Found %ld of %ld rows\n", (ulong) found, (ulong) row_count);
335     maria_scan_end(file);
336   }
337 
338   if (checkpoint == 3 && ma_checkpoint_execute(CHECKPOINT_MEDIUM, FALSE))
339     goto err;
340 
341   if (testflag == 3)
342   {
343     printf("Terminating after updates\n");
344     goto end;
345   }
346   if (!silent)
347     printf("- Reopening file\n");
348   if (maria_commit(file))
349     goto err;
350   if (maria_close(file))
351     goto err;
352   if (!(file=maria_open(filename,2,HA_OPEN_ABORT_IF_LOCKED, 0)))
353     goto err;
354   if (maria_begin(file))
355     goto err;
356   if (opt_versioning)
357     maria_versioning(file, 1);
358   if (!skip_delete)
359   {
360     if (!silent)
361       printf("- Removing keys\n");
362 
363     for (i=0 ; i <= 10 ; i++)
364     {
365       /*
366         If you want to debug the problem in ma_test_recovery with BLOBs
367         (see @todo there), you can break out of the loop after just one
368         delete, it is enough, like this:
369         if (i==1) break;
370       */
371       /* testing */
372       if (remove_count-- == 0)
373       {
374         fprintf(stderr,
375                 "delete-rows number of rows deleted; Going down hard!\n");
376         goto end;
377       }
378       j=i*2;
379       if (!flags[j])
380 	continue;
381       create_key(key,j);
382       my_errno=0;
383       if ((error = maria_rkey(file, read_record, 0, key,
384                               HA_WHOLE_KEY, HA_READ_KEY_EXACT)))
385       {
386 	if (verbose || (flags[j] >= 1 ||
387 			(error && my_errno != HA_ERR_KEY_NOT_FOUND)))
388 	  printf("key: '%.*s'  maria_rkey:  %3d  errno: %3d\n",
389 		 (int) key_length,key+offset_to_key,error,my_errno);
390       }
391       else
392       {
393 	error=maria_delete(file,read_record);
394 	if (verbose || error)
395 	  printf("key: '%.*s'  maria_delete: %3d  errno: %3d\n",
396 		 (int) key_length, key+offset_to_key, error, my_errno);
397 	if (! error)
398 	{
399 	  deleted++;
400 	  flags[j]--;
401 	}
402       }
403     }
404   }
405 
406   if (checkpoint == 4 && ma_checkpoint_execute(CHECKPOINT_MEDIUM, FALSE))
407     goto err;
408 
409   if (testflag == 4)
410   {
411     printf("Terminating after deletes\n");
412     goto end;
413   }
414 
415   if (!silent)
416     printf("- Reading rows with key\n");
417   record[1]= 0;                                 /* For nicer printf */
418 
419   if (record_type == NO_RECORD)
420     maria_extra(file, HA_EXTRA_KEYREAD, 0);
421 
422   for (i=0 ; i <= 25 ; i++)
423   {
424     create_key(key,i);
425     my_errno=0;
426     error=maria_rkey(file,read_record,0,key,HA_WHOLE_KEY,HA_READ_KEY_EXACT);
427     if (verbose ||
428 	(error == 0 && flags[i] == 0 && unique_key) ||
429 	(error && (flags[i] != 0 || my_errno != HA_ERR_KEY_NOT_FOUND)))
430     {
431       printf("key: '%.*s'  maria_rkey: %3d  errno: %3d  record: %s\n",
432 	     (int) key_length,key+offset_to_key,error,my_errno,record+1);
433     }
434   }
435   if (record_type == NO_RECORD)
436   {
437     maria_extra(file, HA_EXTRA_NO_KEYREAD, 0);
438     goto end;
439   }
440 
441   if (!silent)
442     printf("- Reading rows with position\n");
443 
444   if (maria_scan_init(file))
445   {
446     fprintf(stderr, "maria_scan_init failed\n");
447     goto err;
448   }
449 
450   for (i=1,found=0 ; i <= 30 ; i++)
451   {
452     my_errno=0;
453     if ((error= maria_scan(file, read_record)) == HA_ERR_END_OF_FILE)
454     {
455       if (found != row_count-deleted)
456 	printf("Found only %ld of %ld rows\n", (ulong) found,
457 	       (ulong) (row_count - deleted));
458       break;
459     }
460     if (!error)
461       found++;
462     if (verbose || (error != 0 && error != HA_ERR_RECORD_DELETED &&
463 		    error != HA_ERR_END_OF_FILE))
464     {
465       printf("pos: %2d  maria_rrnd: %3d  errno: %3d  record: %s\n",
466 	     i-1,error,my_errno,read_record+1);
467     }
468   }
469   maria_scan_end(file);
470 
471 end:
472   if (die_in_middle_of_transaction)
473   {
474     /* As commit record is not done, UNDO entries needs to be rolled back */
475     switch (die_in_middle_of_transaction) {
476     case 1:
477       /*
478         Flush changed pages go to disk. That will also flush log. Recovery
479         will skip REDOs and apply UNDOs.
480       */
481       _ma_flush_table_files(file, MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
482                             FLUSH_RELEASE, FLUSH_RELEASE);
483       break;
484     case 2:
485       /*
486         Just flush log. Pages are likely to not be on disk. Recovery will
487         then execute REDOs and UNDOs.
488       */
489       if (translog_flush(file->trn->undo_lsn))
490         goto err;
491       break;
492     case 3:
493       /*
494         Flush nothing. Pages and log are likely to not be on disk. Recovery
495         will then do nothing.
496       */
497       break;
498     case 4:
499       /*
500         Flush changed data pages go to disk. Changed index pages are not
501         flushed. Recovery will skip some REDOs and apply UNDOs.
502       */
503       _ma_flush_table_files(file, MARIA_FLUSH_DATA, FLUSH_RELEASE,
504                             FLUSH_RELEASE);
505       /*
506         We have to flush log separately as the redo for the last key page
507         may not be flushed
508       */
509       if (translog_flush(file->trn->undo_lsn))
510         goto err;
511       break;
512     }
513     printf("Dying on request without maria_commit()/maria_close()\n");
514     sf_leaking_memory= 1;
515     exit(0);
516   }
517 
518   if (maria_commit(file))
519     goto err;
520   if (maria_close(file))
521     goto err;
522   maria_end();
523   my_uuid_end();
524   my_end(MY_CHECK_ERROR);
525 
526   return (0);
527 err:
528   printf("got error: %3d when using maria-database\n",my_errno);
529   return 1;			/* skip warning */
530 }
531 
532 
create_key_part(uchar * key,uint rownr)533 static void create_key_part(uchar *key,uint rownr)
534 {
535   if (!unique_key)
536     rownr&=7;					/* Some identical keys */
537   if (keyinfo[0].seg[0].type == HA_KEYTYPE_NUM)
538   {
539     sprintf((char*) key,"%*d",keyinfo[0].seg[0].length,rownr);
540   }
541   else if (keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT1 ||
542            keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT2)
543   {						/* Alpha record */
544     /* Create a key that may be easily packed */
545     bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
546     sprintf((char*) key+keyinfo[0].seg[0].length-2,"%-2d",rownr);
547     if ((rownr & 7) == 0)
548     {
549       /* Change the key to force a unpack of the next key */
550       bfill(key+3,keyinfo[0].seg[0].length-5,rownr < 10 ? 'a' : 'b');
551     }
552   }
553   else
554   {						/* Alpha record */
555     if (keyinfo[0].seg[0].flag & HA_SPACE_PACK)
556       sprintf((char*) key,"%-*d",keyinfo[0].seg[0].length,rownr);
557     else
558     {
559       /* Create a key that may be easily packed */
560       bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
561       sprintf((char*) key+keyinfo[0].seg[0].length-2,"%-2d",rownr);
562       if ((rownr & 7) == 0)
563       {
564 	/* Change the key to force a unpack of the next key */
565 	key[1]= (rownr < 10 ? 'a' : 'b');
566       }
567     }
568   }
569 }
570 
571 
create_key(uchar * key,uint rownr)572 static void create_key(uchar *key,uint rownr)
573 {
574   if (keyinfo[0].seg[0].null_bit)
575   {
576     if (rownr == 0)
577     {
578       key[0]=1;					/* null key */
579       key[1]=0;					/* For easy print of key */
580       return;
581     }
582     *key++=0;
583   }
584   if (keyinfo[0].seg[0].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
585   {
586     size_t tmp;
587     create_key_part(key+2,rownr);
588     tmp=strlen((char*) key+2);
589     int2store(key,tmp);
590   }
591   else
592     create_key_part(key,rownr);
593 }
594 
595 
596 static uchar blob_key[MAX_REC_LENGTH];
597 static uchar blob_record[MAX_REC_LENGTH+20*20];
598 
599 
create_record(uchar * record,uint rownr)600 static void create_record(uchar *record,uint rownr)
601 {
602   uchar *pos;
603   bzero((char*) record,MAX_REC_LENGTH);
604   record[0]=1;					/* delete marker */
605   if (rownr == 0 && keyinfo[0].seg[0].null_bit)
606     record[0]|=keyinfo[0].seg[0].null_bit;	/* Null key */
607 
608   pos=record+1;
609   if (recinfo[0].type == FIELD_BLOB)
610   {
611     size_t tmp;
612     uchar *ptr;
613     create_key_part(blob_key,rownr);
614     tmp=strlen((char*) blob_key);
615     int4store(pos,tmp);
616     ptr=blob_key;
617     memcpy(pos+4,&ptr,sizeof(char*));
618     pos+=recinfo[0].length;
619   }
620   else if (recinfo[0].type == FIELD_VARCHAR)
621   {
622     size_t tmp, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[0].length-1);
623     create_key_part(pos+pack_length,rownr);
624     tmp= strlen((char*) pos+pack_length);
625     if (pack_length == 1)
626       *(uchar*) pos= (uchar) tmp;
627     else
628       int2store(pos,tmp);
629     pos+= recinfo[0].length;
630   }
631   else
632   {
633     create_key_part(pos,rownr);
634     pos+=recinfo[0].length;
635   }
636   if (recinfo[1].type == FIELD_BLOB)
637   {
638     size_t tmp;
639     uchar *ptr;;
640     sprintf((char*) blob_record,"... row: %d", rownr);
641     strappend((char*) blob_record,MY_MAX(MAX_REC_LENGTH-rownr,10),' ');
642     tmp=strlen((char*) blob_record);
643     int4store(pos,tmp);
644     ptr=blob_record;
645     memcpy(pos+4,&ptr,sizeof(char*));
646   }
647   else if (recinfo[1].type == FIELD_VARCHAR)
648   {
649     size_t tmp, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
650     sprintf((char*) pos+pack_length, "... row: %d", rownr);
651     tmp= strlen((char*) pos+pack_length);
652     if (pack_length == 1)
653       *pos= (uchar) tmp;
654     else
655       int2store(pos,tmp);
656   }
657   else
658   {
659     sprintf((char*) pos,"... row: %d", rownr);
660     strappend((char*) pos,recinfo[1].length,' ');
661   }
662 }
663 
664 /* change row to test re-packing of rows and reallocation of keys */
665 
update_record(uchar * record)666 static void update_record(uchar *record)
667 {
668   uchar *pos=record+1;
669   if (recinfo[0].type == FIELD_BLOB)
670   {
671     uchar *column,*ptr;
672     int length;
673     length=uint4korr(pos);			/* Long blob */
674     memcpy(&column,pos+4,sizeof(char*));
675     memcpy(blob_key,column,length);		/* Move old key */
676     ptr=blob_key;
677     memcpy(pos+4,&ptr,sizeof(char*));	/* Store pointer to new key */
678     if (keyinfo[0].seg[0].type != HA_KEYTYPE_NUM)
679       my_ci_casedn(default_charset_info, (char*) blob_key, length,
680                                          (char*) blob_key, length);
681     pos+=recinfo[0].length;
682   }
683   else if (recinfo[0].type == FIELD_VARCHAR)
684   {
685     uint pack_length= HA_VARCHAR_PACKLENGTH(recinfo[0].length-1);
686     uint length= pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos);
687     my_ci_casedn(default_charset_info, (char*) pos + pack_length, length,
688                                        (char*) pos + pack_length, length);
689     pos+=recinfo[0].length;
690   }
691   else
692   {
693     if (keyinfo[0].seg[0].type != HA_KEYTYPE_NUM)
694       my_ci_casedn(default_charset_info, (char*) pos, keyinfo[0].seg[0].length,
695                                          (char*) pos, keyinfo[0].seg[0].length);
696     pos+=recinfo[0].length;
697   }
698 
699   if (recinfo[1].type == FIELD_BLOB)
700   {
701     uchar *column;
702     int length;
703     length=uint4korr(pos);
704     memcpy(&column,pos+4,sizeof(char*));
705     memcpy(blob_record,column,length);
706     bfill(blob_record+length,20,'.');	/* Make it larger */
707     length+=20;
708     int4store(pos,length);
709     column=blob_record;
710     memcpy(pos+4,&column,sizeof(char*));
711   }
712   else if (recinfo[1].type == FIELD_VARCHAR)
713   {
714     /* Second field is longer than 10 characters */
715     uint pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
716     uint length= pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos);
717     pos= record+ recinfo[1].offset;
718     bfill(pos+pack_length+length,recinfo[1].length-length-pack_length,'.');
719     length=recinfo[1].length-pack_length;
720     if (pack_length == 1)
721       *(uchar*) pos= (uchar) length;
722     else
723       int2store(pos,length);
724   }
725   else
726   {
727     bfill(pos+recinfo[1].length-10,10,'.');
728   }
729 }
730 
731 
732 static struct my_option my_long_options[] =
733 {
734   {"checkpoint", 'H', "Checkpoint at specified stage", (uchar**) &checkpoint,
735    (uchar**) &checkpoint, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
736   {"checksum", 'c', "Undocumented",
737    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
738 #ifndef DBUG_OFF
739   {"debug", '#', "Undocumented",
740    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
741 #endif
742   {"datadir", 'h', "Path to the database root.", (char**) &maria_data_root,
743    (char**) &maria_data_root, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
744   {"delete-rows", 'd', "Abort after this many rows has been deleted",
745    (uchar**) &remove_count, (uchar**) &remove_count, 0, GET_UINT, REQUIRED_ARG,
746    1000, 0, 0, 0, 0, 0},
747   {"help", '?', "Display help and exit",
748    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
749   {"insert-rows", 'i', "Undocumented", (uchar**) &insert_count,
750    (uchar**) &insert_count, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
751   {"key-alpha", 'a', "Use a key of type HA_KEYTYPE_TEXT",
752    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
753   {"key-binary-pack", 'B', "Undocumented",
754    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
755   {"key-blob", 'b', "Undocumented",
756    (uchar**) &blob_length, (uchar**) &blob_length,
757    0, GET_ULONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
758   {"key-cache", 'K', "Undocumented", (uchar**) &pagecacheing,
759    (uchar**) &pagecacheing, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
760   {"key-length", 'k', "Undocumented", (uchar**) &key_length,
761    (uchar**) &key_length, 0, GET_UINT, REQUIRED_ARG, 6, 0, 0, 0, 0, 0},
762   {"key-multiple", 'm', "Don't use unique keys",
763    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
764   {"key-prefix_pack", 'P', "Undocumented",
765    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
766   {"key-space_pack", 'p', "Undocumented",
767    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
768   {"key-varchar", 'w', "Test VARCHAR keys",
769    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
770   {"null-fields", 'N', "Define fields with NULL",
771    (uchar**) &null_fields, (uchar**) &null_fields, 0, GET_BOOL, NO_ARG,
772    0, 0, 0, 0, 0, 0},
773   {"row-fixed-size", 'S', "Fixed size records",
774    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
775   {"rows-in-block", 'M', "Store rows in block format",
776    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
777   {"rows-no-data", 'n', "Don't store any data, only keys",
778    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
779   {"row-pointer-size", 'R', "Undocumented", (uchar**) &rec_pointer_size,
780    (uchar**) &rec_pointer_size, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
781   {"silent", 's', "Undocumented",
782    (uchar**) &silent, (uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
783    0, 0},
784   {"skip-delete", 'D', "Don't test deletes", (uchar**) &skip_delete,
785    (uchar**) &skip_delete, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
786   {"skip-update", 'U', "Don't test updates", (uchar**) &skip_update,
787    (uchar**) &skip_update, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
788   {"testflag", 't', "Stop test at specified stage", (uchar**) &testflag,
789    (uchar**) &testflag, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
790   {"test-undo", 'A',
791    "Abort hard. Used for testing recovery with undo",
792    (uchar**) &die_in_middle_of_transaction,
793    (uchar**) &die_in_middle_of_transaction,
794    0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
795   {"transactional", 'T',
796    "Test in transactional mode. (Only works with block format)",
797    (uchar**) &transactional, (uchar**) &transactional, 0, GET_BOOL, NO_ARG,
798    0, 0, 0, 0, 0, 0},
799   {"unique", 'E', "Check unique handling", (uchar**) &opt_unique,
800    (uchar**) &opt_unique, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
801   {"update-rows", 'u', "Max number of rows to update", (uchar**) &update_count,
802    (uchar**) &update_count, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
803   {"verbose", 'v', "Be more verbose", (uchar**) &verbose,
804    (uchar**) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
805   {"version", 'V', "Print version number and exit",
806    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
807   {"versioning", 'C', "Use row versioning (only works with block format)",
808    (uchar**) &opt_versioning,  (uchar**) &opt_versioning, 0, GET_BOOL,
809    NO_ARG, 0, 0, 0, 0, 0, 0},
810   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
811 };
812 
813 
814 static my_bool
get_one_option(const struct my_option * opt,const char * argument,const char * filename)815 get_one_option(const struct my_option *opt,
816 	       const char *argument __attribute__((unused)),
817                const char *filename __attribute__((unused)))
818 {
819   switch(opt->id) {
820   case 'a':
821     key_type= HA_KEYTYPE_TEXT;
822     break;
823   case 'c':
824     create_flag|= HA_CREATE_CHECKSUM | HA_CREATE_PAGE_CHECKSUM;
825     break;
826   case 'R':				/* Length of record pointer */
827     if (rec_pointer_size > 3)
828       rec_pointer_size=0;
829     break;
830   case 'P':
831     pack_keys= HA_PACK_KEY;		/* Use prefix compression */
832     break;
833   case 'B':
834     pack_keys= HA_BINARY_PACK_KEY;	/* Use binary compression */
835     break;
836   case 'M':
837     record_type= BLOCK_RECORD;
838     break;
839   case 'n':
840     record_type= NO_RECORD;
841     break;
842   case 'S':
843     if (key_field == FIELD_VARCHAR)
844     {
845       create_flag=0;			/* Static sized varchar */
846       record_type= STATIC_RECORD;
847     }
848     else if (key_field != FIELD_BLOB)
849     {
850       key_field=FIELD_NORMAL;		/* static-size record */
851       extra_field=FIELD_NORMAL;
852       record_type= STATIC_RECORD;
853     }
854     break;
855   case 'p':
856     pack_keys=HA_PACK_KEY;		/* Use prefix + space packing */
857     pack_seg=HA_SPACE_PACK;
858     key_type=HA_KEYTYPE_TEXT;
859     break;
860   case 'm':
861     unique_key=0;
862     break;
863   case 'b':
864     key_field=FIELD_BLOB;			/* blob key */
865     extra_field= FIELD_BLOB;
866     pack_seg|= HA_BLOB_PART;
867     key_type= HA_KEYTYPE_VARTEXT1;
868     if (record_type == STATIC_RECORD)
869       record_type= DYNAMIC_RECORD;
870     break;
871   case 'k':
872     if (key_length < 4 || key_length > MARIA_MAX_KEY_LENGTH)
873     {
874       fprintf(stderr,"Wrong key length\n");
875       exit(1);
876     }
877     break;
878   case 'w':
879     key_field=FIELD_VARCHAR;			/* varchar keys */
880     extra_field= FIELD_VARCHAR;
881     key_type= HA_KEYTYPE_VARTEXT1;
882     pack_seg|= HA_VAR_LENGTH_PART;
883     if (record_type == STATIC_RECORD)
884       record_type= DYNAMIC_RECORD;
885     break;
886   case 'K':                                     /* Use key cacheing */
887     pagecacheing=1;
888     break;
889   case 'V':
890     printf("test1 Ver 1.2 \n");
891     exit(0);
892   case '#':
893     DBUG_PUSH(argument);
894     break;
895   case '?':
896     usage();
897     exit(1);
898   }
899   return 0;
900 }
901 
902 
903 /* Read options */
904 
get_options(int argc,char * argv[])905 static void get_options(int argc, char *argv[])
906 {
907   int ho_error;
908 
909   if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
910     exit(ho_error);
911   if (transactional)
912     record_type= BLOCK_RECORD;
913   if (record_type == NO_RECORD)
914     skip_update= skip_delete= 1;
915 
916 
917   return;
918 } /* get options */
919 
920 
usage()921 static void usage()
922 {
923   printf("Usage: %s [options]\n\n", my_progname);
924   my_print_help(my_long_options);
925   my_print_variables(my_long_options);
926 }
927 
928 #include "ma_check_standalone.h"
929 
930