1 /* Copyright (C) 2018, 2021, MariaDB corporation.
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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software Foundation,
14    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15 
16 /* Code for doing backups of Aria tables */
17 
18 /******************************************************************************
19   Testing ma_backup interface
20   Table creation code is taken from ma_test1
21 ******************************************************************************/
22 
23 #define ROWS_IN_TEST 100000
24 
25 #include "maria_def.h"
26 #include "ma_blockrec.h"                        /* PAGE_SUFFIX_SIZE */
27 #include "ma_checkpoint.h"
28 #include <aria_backup.h>
29 
30 static int silent;
31 static int create_test_table(const char *table_name, int stage);
32 static int copy_table(const char *table_name, int stage);
33 static void create_record(uchar *record,uint rownr);
34 
main(int argc,char * argv[])35 int main(int argc __attribute__((unused)), char *argv[])
36 {
37   int error= 1;
38   int i;
39   char buff[FN_REFLEN];
40 #ifdef SAFE_MUTEX
41   safe_mutex_deadlock_detector= 1;
42 #endif
43   MY_INIT(argv[0]);
44   maria_data_root= ".";
45 
46   /* Maria requires that we always have a page cache */
47   if (maria_init() ||
48       (init_pagecache(maria_pagecache, maria_block_size * 2000, 0, 0,
49                       maria_block_size, 0, MY_WME) == 0) ||
50       ma_control_file_open(TRUE, TRUE, TRUE) ||
51       (init_pagecache(maria_log_pagecache,
52                       TRANSLOG_PAGECACHE_SIZE, 0, 0,
53                       TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0) ||
54       translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
55                     0, 0, maria_log_pagecache,
56                     TRANSLOG_DEFAULT_FLAGS, 0) ||
57       (trnman_init(0) || ma_checkpoint_init(0)))
58   {
59     fprintf(stderr, "Error in initialization\n");
60     exit(1);
61   }
62   init_thr_lock();
63 
64   fn_format(buff, "test_copy", maria_data_root, "", MYF(0));
65 
66   for (i= 0; i < 5 ; i++)
67   {
68     printf("Stage: %d\n", i);
69     fflush(stdout);
70     if (create_test_table(buff, i))
71       goto err;
72     if (copy_table(buff, i))
73       goto err;
74   }
75   error= 0;
76   printf("test ok\n");
77 err:
78   if (error)
79     fprintf(stderr, "Test %i failed\n", i);
80   maria_end();
81   my_uuid_end();
82   my_end(MY_CHECK_ERROR);
83   exit(error);
84 }
85 
86 
87 /**
88    Example of how to read an Aria table
89 */
90 
copy_table(const char * table_name,int stage)91 static int copy_table(const char *table_name, int stage)
92 {
93   char old_name[FN_REFLEN];
94   uchar *copy_buffer= 0;
95   ARIA_TABLE_CAPABILITIES cap;
96   ulonglong block;
97   File org_file= -1;
98   int error= 1;
99 
100   strxmov(old_name, table_name, ".MAI", NullS);
101 
102   if ((org_file= my_open(old_name,
103                          O_RDONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
104                          MYF(MY_WME))) < 0)
105     goto err;
106   if ((error= aria_get_capabilities(org_file, &cap)))
107   {
108     fprintf(stderr, "aria_get_capabilities failed:  %d\n", error);
109     goto err;
110   }
111 
112   printf("- Capabilities read. oneline_backup_safe: %d\n",
113          cap.online_backup_safe);
114   printf("- Copying index file\n");
115 
116   copy_buffer= my_malloc(PSI_NOT_INSTRUMENTED, cap.block_size, MYF(0));
117   for (block= 0 ; ; block++)
118   {
119     if ((error= aria_read_index(org_file, &cap, block, copy_buffer) ==
120          HA_ERR_END_OF_FILE))
121       break;
122     if (error)
123     {
124       fprintf(stderr, "aria_read_index failed:  %d\n", error);
125       goto err;
126     }
127   }
128   my_close(org_file, MYF(MY_WME));
129 
130 
131   printf("- Copying data file\n");
132   strxmov(old_name, table_name, ".MAD", NullS);
133   if ((org_file= my_open(old_name, O_RDONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
134                          MYF(MY_WME))) < 0)
135     goto err;
136 
137   for (block= 0 ; ; block++)
138   {
139     size_t length;
140     if ((error= aria_read_data(org_file, &cap, block, copy_buffer,
141                                &length) == HA_ERR_END_OF_FILE))
142       break;
143     if (error)
144     {
145       fprintf(stderr, "aria_read_index failed:  %d\n", error);
146       goto err;
147     }
148   }
149   error= 0;
150 
151 err:
152   my_free(copy_buffer);
153   if (org_file >= 0)
154     my_close(org_file, MYF(MY_WME));
155   if (error)
156     fprintf(stderr, "Failed in copy_table stage: %d\n", stage);
157   return error;
158 }
159 
160 
161 /* Code extracted from ma_test1.c */
162 #define MAX_REC_LENGTH 1024
163 
164 static MARIA_COLUMNDEF recinfo[4];
165 static MARIA_KEYDEF keyinfo[10];
166 static HA_KEYSEG keyseg[10];
167 static HA_KEYSEG uniqueseg[10];
168 
169 
170 /**
171    Create a test table and fill it with some data
172 */
173 
create_test_table(const char * table_name,int type_of_table)174 static int create_test_table(const char *table_name, int type_of_table)
175 {
176   MARIA_HA *file;
177   int i,error,uniques=0;
178   int key_field=FIELD_SKIP_PRESPACE,extra_field=FIELD_SKIP_ENDSPACE;
179   int key_type=HA_KEYTYPE_NUM;
180   int create_flag=0;
181   uint pack_seg=0, pack_keys= 0;
182   uint key_length;
183   uchar record[MAX_REC_LENGTH];
184   MARIA_UNIQUEDEF uniquedef;
185   MARIA_CREATE_INFO create_info;
186   enum data_file_type record_type= DYNAMIC_RECORD;
187   my_bool null_fields= 0, unique_key= 0;
188   my_bool opt_unique= 0;
189   my_bool transactional= 0;
190 
191   key_length= 12;
192   switch (type_of_table) {
193   case 0:
194     break;
195   case 1:
196     create_flag|= HA_CREATE_CHECKSUM | HA_CREATE_PAGE_CHECKSUM;
197     break;
198   case 2:                                       /* transactional */
199     create_flag|= HA_CREATE_CHECKSUM | HA_CREATE_PAGE_CHECKSUM;
200     record_type= BLOCK_RECORD;
201     transactional= 1;
202     break;
203   case 3:                                       /* transactional */
204     create_flag|= HA_CREATE_CHECKSUM | HA_CREATE_PAGE_CHECKSUM;
205     record_type= BLOCK_RECORD;
206     transactional= 1;
207     key_field=FIELD_VARCHAR;			/* varchar keys */
208     extra_field= FIELD_VARCHAR;
209     key_type= HA_KEYTYPE_VARTEXT1;
210     pack_seg|= HA_VAR_LENGTH_PART;
211     null_fields= 1;
212     break;
213   case 4:                                       /* transactional */
214     create_flag|= HA_CREATE_CHECKSUM | HA_CREATE_PAGE_CHECKSUM;
215     record_type= BLOCK_RECORD;
216     transactional= 1;
217     key_field=FIELD_BLOB;			/* blob key */
218     extra_field= FIELD_BLOB;
219     pack_seg|= HA_BLOB_PART;
220     key_type= HA_KEYTYPE_VARTEXT1;
221     break;
222   }
223 
224 
225   bzero((char*) recinfo,sizeof(recinfo));
226   bzero((char*) &create_info,sizeof(create_info));
227 
228   /* First define 2 columns */
229   create_info.null_bytes= 1;
230   recinfo[0].type= key_field;
231   recinfo[0].length= (key_field == FIELD_BLOB ? 4+portable_sizeof_char_ptr :
232 		      key_length);
233   if (key_field == FIELD_VARCHAR)
234     recinfo[0].length+= HA_VARCHAR_PACKLENGTH(key_length);
235   recinfo[1].type=extra_field;
236   recinfo[1].length= (extra_field == FIELD_BLOB ? 4 + portable_sizeof_char_ptr : 24);
237   if (extra_field == FIELD_VARCHAR)
238     recinfo[1].length+= HA_VARCHAR_PACKLENGTH(recinfo[1].length);
239   recinfo[1].null_bit= null_fields ? 2 : 0;
240 
241   if (opt_unique)
242   {
243     recinfo[2].type=FIELD_CHECK;
244     recinfo[2].length=MARIA_UNIQUE_HASH_LENGTH;
245   }
246 
247   if (key_type == HA_KEYTYPE_VARTEXT1 &&
248       key_length > 255)
249     key_type= HA_KEYTYPE_VARTEXT2;
250 
251   /* Define a key over the first column */
252   keyinfo[0].seg=keyseg;
253   keyinfo[0].keysegs=1;
254   keyinfo[0].block_length= 0;                   /* Default block length */
255   keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
256   keyinfo[0].seg[0].type= key_type;
257   keyinfo[0].seg[0].flag= pack_seg;
258   keyinfo[0].seg[0].start=1;
259   keyinfo[0].seg[0].length=key_length;
260   keyinfo[0].seg[0].null_bit= null_fields ? 2 : 0;
261   keyinfo[0].seg[0].null_pos=0;
262   keyinfo[0].seg[0].language= default_charset_info->number;
263   if (pack_seg & HA_BLOB_PART)
264   {
265     keyinfo[0].seg[0].bit_start=4;		/* Length of blob length */
266   }
267   keyinfo[0].flag = (uint8) (pack_keys | unique_key);
268 
269   if (opt_unique)
270   {
271     uint start;
272     uniques=1;
273     bzero((char*) &uniquedef,sizeof(uniquedef));
274     bzero((char*) uniqueseg,sizeof(uniqueseg));
275     uniquedef.seg=uniqueseg;
276     uniquedef.keysegs=2;
277 
278     /* Make a unique over all columns (except first NULL fields) */
279     for (i=0, start=1 ; i < 2 ; i++)
280     {
281       uniqueseg[i].start=start;
282       start+=recinfo[i].length;
283       uniqueseg[i].length=recinfo[i].length;
284       uniqueseg[i].language= default_charset_info->number;
285     }
286     uniqueseg[0].type= key_type;
287     uniqueseg[0].null_bit= null_fields ? 2 : 0;
288     uniqueseg[1].type= HA_KEYTYPE_TEXT;
289     if (extra_field == FIELD_BLOB)
290     {
291       uniqueseg[1].length=0;			/* The whole blob */
292       uniqueseg[1].bit_start=4;			/* long blob */
293       uniqueseg[1].flag|= HA_BLOB_PART;
294     }
295     else if (extra_field == FIELD_VARCHAR)
296     {
297       uniqueseg[1].flag|= HA_VAR_LENGTH_PART;
298       uniqueseg[1].type= (HA_VARCHAR_PACKLENGTH(recinfo[1].length-1) == 1 ?
299                           HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2);
300     }
301   }
302   else
303     uniques=0;
304 
305   if (!silent)
306     printf("- Creating Aria file\n");
307   create_info.max_rows= 0;
308   create_info.transactional= transactional;
309   if (maria_create(table_name, record_type, 1, keyinfo,2+opt_unique,recinfo,
310 		uniques, &uniquedef, &create_info,
311 		create_flag))
312     goto err;
313   if (!(file=maria_open(table_name,2,HA_OPEN_ABORT_IF_LOCKED, 0)))
314     goto err;
315   if (!silent)
316     printf("- Writing key:s\n");
317 
318   if (maria_begin(file))
319     goto err;
320   my_errno=0;
321   for (i= 0 ; i < ROWS_IN_TEST ; i++)
322   {
323     create_record(record,i);
324     if ((error=maria_write(file,record)))
325       goto err;
326   }
327 
328   if (maria_commit(file) | maria_close(file))
329     goto err;
330   printf("- Data copied\n");
331   return 0;
332 
333 err:
334   printf("got error: %3d when using maria-database\n",my_errno);
335   return 1;			/* skip warning */
336 }
337 
338 
create_key_part(uchar * key,uint rownr)339 static void create_key_part(uchar *key,uint rownr)
340 {
341   if (keyinfo[0].seg[0].type == HA_KEYTYPE_NUM)
342   {
343     sprintf((char*) key,"%*d",keyinfo[0].seg[0].length,rownr);
344   }
345   else if (keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT1 ||
346            keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT2)
347   {						/* Alpha record */
348     /* Create a key that may be easily packed */
349     bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
350     sprintf((char*) key+keyinfo[0].seg[0].length-2,"%-2d",rownr % 100);
351     if ((rownr & 7) == 0)
352     {
353       /* Change the key to force a unpack of the next key */
354       bfill(key+3,keyinfo[0].seg[0].length-5,rownr < 10 ? 'a' : 'b');
355     }
356   }
357   else
358   {						/* Alpha record */
359     if (keyinfo[0].seg[0].flag & HA_SPACE_PACK)
360       sprintf((char*) key,"%-*d",keyinfo[0].seg[0].length,rownr);
361     else
362     {
363       /* Create a key that may be easily packed */
364       bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
365       sprintf((char*) key+keyinfo[0].seg[0].length-2,"%-2d",rownr % 100);
366       if ((rownr & 7) == 0)
367       {
368 	/* Change the key to force a unpack of the next key */
369 	key[1]= (rownr < 10 ? 'a' : 'b');
370       }
371     }
372   }
373 }
374 
375 
376 static uchar blob_key[MAX_REC_LENGTH];
377 static uchar blob_record[MAX_REC_LENGTH+20*20];
378 
379 
create_record(uchar * record,uint rownr)380 static void create_record(uchar *record,uint rownr)
381 {
382   uchar *pos;
383   bzero((char*) record,MAX_REC_LENGTH);
384   record[0]=1;					/* delete marker */
385   if (rownr == 0 && keyinfo[0].seg[0].null_bit)
386     record[0]|=keyinfo[0].seg[0].null_bit;	/* Null key */
387 
388   pos=record+1;
389   if (recinfo[0].type == FIELD_BLOB)
390   {
391     size_t tmp;
392     uchar *ptr;
393     create_key_part(blob_key,rownr);
394     tmp=strlen((char*) blob_key);
395     int4store(pos,tmp);
396     ptr=blob_key;
397     memcpy(pos+4,&ptr,sizeof(char*));
398     pos+=recinfo[0].length;
399   }
400   else if (recinfo[0].type == FIELD_VARCHAR)
401   {
402     size_t tmp, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[0].length-1);
403     create_key_part(pos+pack_length,rownr);
404     tmp= strlen((char*) pos+pack_length);
405     if (pack_length == 1)
406       *(uchar*) pos= (uchar) tmp;
407     else
408       int2store(pos,tmp);
409     pos+= recinfo[0].length;
410   }
411   else
412   {
413     create_key_part(pos,rownr);
414     pos+=recinfo[0].length;
415   }
416   if (recinfo[1].type == FIELD_BLOB)
417   {
418     size_t tmp;
419     uchar *ptr;;
420     sprintf((char*) blob_record,"... row: %d", rownr);
421     strappend((char*) blob_record, rownr % MAX_REC_LENGTH,'x');
422     tmp=strlen((char*) blob_record);
423     int4store(pos,tmp);
424     ptr=blob_record;
425     memcpy(pos+4,&ptr,sizeof(char*));
426   }
427   else if (recinfo[1].type == FIELD_VARCHAR)
428   {
429     size_t tmp, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
430     sprintf((char*) pos+pack_length, "... row: %d", rownr);
431     tmp= strlen((char*) pos+pack_length);
432     if (pack_length == 1)
433       *pos= (uchar) tmp;
434     else
435       int2store(pos,tmp);
436   }
437   else
438   {
439     sprintf((char*) pos,"... row: %d", rownr);
440     strappend((char*) pos,recinfo[1].length,' ');
441   }
442 }
443 
444 #include "ma_check_standalone.h"
445