1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
2    Copyright (c) 2009, 2018, 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 St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 /*
18   locking of isam-tables.
19   reads info from a isam-table. Must be first request before doing any furter
20   calls to any isamfunktion.  Is used to allow many process use the same
21   isamdatabase.
22 */
23 
24 #include "ftdefs.h"
25 
26 static void mi_update_status_with_lock(MI_INFO *info);
27 
28 	/* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
29 
mi_lock_database(MI_INFO * info,int lock_type)30 int mi_lock_database(MI_INFO *info, int lock_type)
31 {
32   int error, mark_crashed= 0;
33   uint count;
34   MYISAM_SHARE *share=info->s;
35   DBUG_ENTER("mi_lock_database");
36   DBUG_PRINT("enter",("lock_type: %d  old lock %d  r_locks: %u  w_locks: %u "
37                       "global_changed:  %d  open_count: %u  name: '%s'",
38                       lock_type, info->lock_type, share->r_locks,
39                       share->w_locks,
40                       share->global_changed, share->state.open_count,
41                       share->index_file_name));
42   if (share->options & HA_OPTION_READ_ONLY_DATA ||
43       info->lock_type == lock_type)
44     DBUG_RETURN(0);
45   if (lock_type == F_EXTRA_LCK)                 /* Used by TMP tables */
46   {
47     ++share->w_locks;
48     ++share->tot_locks;
49     info->lock_type= lock_type;
50     info->s->in_use= list_add(info->s->in_use, &info->in_use);
51     DBUG_RETURN(0);
52   }
53 
54   error= 0;
55   DBUG_EXECUTE_IF ("mi_lock_database_failure", error= EINVAL;);
56   if (!info->intern_lock_locked)
57     mysql_mutex_lock(&share->intern_lock);
58   if (share->kfile >= 0)		/* May only be false on windows */
59   {
60     switch (lock_type) {
61     case F_UNLCK:
62       ftparser_call_deinitializer(info);
63       if (info->lock_type == F_RDLCK)
64       {
65 	count= --share->r_locks;
66         mi_restore_status(info);
67       }
68       else
69       {
70 	count= --share->w_locks;
71         mi_update_status_with_lock(info);
72       }
73       --share->tot_locks;
74       if (info->lock_type == F_WRLCK && !share->w_locks &&
75 	  !share->delay_key_write && flush_key_blocks(share->key_cache,
76 						      share->kfile,
77                                                       &share->dirty_part_map,
78                                                       FLUSH_KEEP))
79       {
80 	mark_crashed= error=my_errno;
81         mi_print_error(info->s, HA_ERR_CRASHED);
82       }
83       if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
84       {
85 	if (end_io_cache(&info->rec_cache))
86 	{
87 	  mark_crashed= error=my_errno;
88           mi_print_error(info->s, HA_ERR_CRASHED);
89 	}
90       }
91       if (!count)
92       {
93 	DBUG_PRINT("info",("changed: %u  w_locks: %u",
94 			   (uint) share->changed, share->w_locks));
95 	if (share->changed && !share->w_locks)
96 	{
97 #ifdef HAVE_MMAP
98           if ((info->s->mmaped_length != info->s->state.state.data_file_length) &&
99               (info->s->nonmmaped_inserts > MAX_NONMAPPED_INSERTS))
100           {
101             if (info->s->concurrent_insert)
102               mysql_rwlock_wrlock(&info->s->mmap_lock);
103             mi_remap_file(info, info->s->state.state.data_file_length);
104             info->s->nonmmaped_inserts= 0;
105             if (info->s->concurrent_insert)
106               mysql_rwlock_unlock(&info->s->mmap_lock);
107           }
108 #endif
109 	  share->state.process= share->last_process=share->this_process;
110 	  share->state.unique=   info->last_unique=  info->this_unique;
111 	  share->state.update_count= info->last_loop= ++info->this_loop;
112           if (mi_state_info_write(share->kfile, &share->state, 1))
113 	    mark_crashed= error=my_errno;
114 	  share->changed=0;
115 	  if (myisam_flush)
116 	  {
117             if (share->file_map)
118               my_msync(info->dfile, share->file_map, share->mmaped_length, MS_SYNC);
119             if (mysql_file_sync(share->kfile, MYF(0)))
120 	      mark_crashed= error= my_errno;
121             if (mysql_file_sync(info->dfile, MYF(0)))
122 	      mark_crashed= error= my_errno;
123 	  }
124 	  else
125 	    share->not_flushed=1;
126 	  if (error)
127             mi_print_error(info->s, HA_ERR_CRASHED);
128 	}
129 	if (info->lock_type != F_EXTRA_LCK)
130 	{
131 	  if (share->r_locks)
132 	  {					/* Only read locks left */
133 	    if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
134 			MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
135 	      error=my_errno;
136 	  }
137 	  else if (!share->w_locks)
138 	  {					/* No more locks */
139 	    if (my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
140 			MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
141 	      error=my_errno;
142 	  }
143 	}
144       }
145       info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
146       info->lock_type= F_UNLCK;
147       info->s->in_use= list_delete(info->s->in_use, &info->in_use);
148       break;
149     case F_RDLCK:
150       if (info->lock_type == F_WRLCK)
151       {
152         /*
153           Change RW to READONLY
154 
155           mysqld does not turn write locks to read locks,
156           so we're never here in mysqld.
157         */
158 	if (share->w_locks == 1)
159 	{
160           if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
161 		      MYF(MY_SEEK_NOT_DONE)))
162 	  {
163 	    error=my_errno;
164 	    break;
165 	  }
166 	}
167 	share->w_locks--;
168 	share->r_locks++;
169 	info->lock_type=lock_type;
170 	break;
171       }
172       if (!share->r_locks && !share->w_locks)
173       {
174 	if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
175 		    info->lock_wait | MY_SEEK_NOT_DONE))
176 	{
177 	  error=my_errno;
178 	  break;
179 	}
180 	if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
181 	{
182 	  error=my_errno;
183 	  (void) my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
184 	  my_errno=error;
185 	  break;
186 	}
187       }
188       (void) _mi_test_if_changed(info);
189       share->r_locks++;
190       share->tot_locks++;
191       info->lock_type=lock_type;
192       info->s->in_use= list_add(info->s->in_use, &info->in_use);
193       break;
194     case F_WRLCK:
195       if (info->lock_type == F_RDLCK)
196       {						/* Change READONLY to RW */
197 	if (share->r_locks == 1)
198 	{
199 	  if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
200 		      MYF(info->lock_wait | MY_SEEK_NOT_DONE)))
201 	  {
202 	    error=my_errno;
203 	    break;
204 	  }
205 	  share->r_locks--;
206 	  share->w_locks++;
207 	  info->lock_type=lock_type;
208 	  break;
209 	}
210       }
211       if (!(share->options & HA_OPTION_READ_ONLY_DATA))
212       {
213 	if (!share->w_locks)
214 	{
215 	  if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
216 		      info->lock_wait | MY_SEEK_NOT_DONE))
217 	  {
218 	    error=my_errno;
219 	    break;
220 	  }
221 	  if (!share->r_locks)
222 	  {
223 	    if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
224 	    {
225 	      error=my_errno;
226 	      (void) my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
227 			   info->lock_wait | MY_SEEK_NOT_DONE);
228 	      my_errno=error;
229 	      break;
230 	    }
231 	  }
232 	}
233       }
234       (void) _mi_test_if_changed(info);
235 
236       info->lock_type=lock_type;
237       info->invalidator=info->s->invalidator;
238       share->w_locks++;
239       share->tot_locks++;
240 
241       DBUG_EXECUTE_IF("simulate_incorrect_share_wlock_value",
242                       DEBUG_SYNC_C("after_share_wlock_increment"););
243 
244       info->s->in_use= list_add(info->s->in_use, &info->in_use);
245       break;
246     default:
247       break;				/* Impossible */
248     }
249   }
250 #ifdef _WIN32
251   else
252   {
253     /*
254        Check for bad file descriptors if this table is part
255        of a merge union. Failing to capture this may cause
256        a crash on windows if the table is renamed and
257        later on referenced by the merge table.
258      */
259     if ((info->open_flag & HA_OPEN_MERGE_TABLE) && (info->s)->kfile < 0)
260     {
261       error = HA_ERR_NO_SUCH_TABLE;
262     }
263   }
264 #endif
265   if (!info->intern_lock_locked)
266     mysql_mutex_unlock(&share->intern_lock);
267   if (mark_crashed)
268     mi_mark_crashed(info);
269   DBUG_RETURN(error);
270 } /* mi_lock_database */
271 
272 
273 /****************************************************************************
274   The following functions are called by thr_lock() in threaded applications
275 ****************************************************************************/
276 
277 /*
278   Create a copy of the current status for the table
279 
280   SYNOPSIS
281     mi_get_status()
282     param		Pointer to Myisam handler
283     concurrent_insert	Set to 1 if we are going to do concurrent inserts
284 			(THR_WRITE_CONCURRENT_INSERT was used)
285 */
286 
mi_get_status(void * param,my_bool concurrent_insert)287 my_bool mi_get_status(void* param, my_bool concurrent_insert)
288 {
289   MI_INFO *info=(MI_INFO*) param;
290   DBUG_ENTER("mi_get_status");
291   DBUG_PRINT("info",("name: %s  key_file: %lu  data_file: %lu  rows: %lu  concurrent_insert: %d",
292                      info->s->index_file_name,
293 		     (ulong) info->s->state.state.key_file_length,
294 		     (ulong) info->s->state.state.data_file_length,
295 		     (ulong) info->s->state.state.records,
296                      concurrent_insert));
297 #ifndef DBUG_OFF
298   if (info->state->key_file_length > info->s->state.state.key_file_length ||
299       info->state->data_file_length > info->s->state.state.data_file_length)
300     DBUG_PRINT("warning",("old info:  key_file: %ld  data_file: %ld",
301 			  (long) info->state->key_file_length,
302 			  (long) info->state->data_file_length));
303 #endif
304   info->save_state=info->s->state.state;
305   info->state= &info->save_state;
306   info->append_insert_at_end= concurrent_insert;
307   if (concurrent_insert)
308     info->s->state.state.uncacheable= TRUE;
309   DBUG_RETURN(0);
310 }
311 
312 
mi_update_status(void * param)313 void mi_update_status(void* param)
314 {
315   MI_INFO *info=(MI_INFO*) param;
316   DBUG_ENTER("mi_update_status");
317   /*
318     Because someone may have closed the table we point at, we only
319     update the state if its our own state.  This isn't a problem as
320     we are always pointing at our own lock or at a read lock.
321     (This is enforced by thr_multi_lock.c)
322   */
323   if (info->state == &info->save_state)
324   {
325     DBUG_PRINT("info",
326                ("updating status:  key_file: %lu  data_file: %lu  rows: %lu",
327                 (ulong) info->state->key_file_length,
328                 (ulong) info->state->data_file_length,
329                 (ulong) info->state->records));
330     if (info->state->key_file_length < info->s->state.state.key_file_length ||
331 	info->state->data_file_length < info->s->state.state.data_file_length)
332       DBUG_PRINT("warning",("old info:  key_file: %ld  data_file: %ld",
333 			    (long) info->s->state.state.key_file_length,
334 			    (long) info->s->state.state.data_file_length));
335     info->s->state.state= *info->state;
336 #ifdef HAVE_QUERY_CACHE
337     DBUG_PRINT("info", ("invalidator... '%s' (status update)",
338                         info->filename));
339     DBUG_ASSERT(info->s->chst_invalidator != NULL);
340     (*info->s->chst_invalidator)((const char *)info->filename);
341 #endif
342   }
343 
344   info->state= &info->s->state.state;
345   info->append_insert_at_end= 0;
346 
347   /*
348     We have to flush the write cache here as other threads may start
349     reading the table before mi_lock_database() is called
350   */
351   if (info->opt_flag & WRITE_CACHE_USED)
352   {
353     if (end_io_cache(&info->rec_cache))
354     {
355       mi_print_error(info->s, HA_ERR_CRASHED);
356       mi_mark_crashed(info);
357     }
358     info->opt_flag&= ~WRITE_CACHE_USED;
359   }
360   DBUG_VOID_RETURN;
361 }
362 
363 /*
364   Same as mi_update_status() but take a lock in the table lock, to protect
365   against someone calling mi_get_status() from thr_lock() at the same time.
366 */
367 
mi_update_status_with_lock(MI_INFO * info)368 static void mi_update_status_with_lock(MI_INFO *info)
369 {
370   my_bool locked= 0;
371   if (info->state == &info->save_state)
372   {
373     locked= 1;
374     mysql_mutex_lock(&info->s->lock.mutex);
375   }
376   mi_update_status(info);
377   if (locked)
378     mysql_mutex_unlock(&info->s->lock.mutex);
379 }
380 
381 
mi_restore_status(void * param)382 void mi_restore_status(void *param)
383 {
384   MI_INFO *info= (MI_INFO*) param;
385   DBUG_ENTER("mi_restore_status");
386   DBUG_PRINT("info",("key_file: %ld  data_file: %ld",
387 		     (long) info->s->state.state.key_file_length,
388 		     (long) info->s->state.state.data_file_length));
389   info->state= &info->s->state.state;
390   info->append_insert_at_end= 0;
391   DBUG_VOID_RETURN;
392 }
393 
394 
mi_copy_status(void * to,void * from)395 void mi_copy_status(void* to,void *from)
396 {
397   MI_INFO *info= (MI_INFO*) to;
398   DBUG_ENTER("mi_copy_status");
399   info->state= &((MI_INFO*) from)->save_state;
400   DBUG_PRINT("info",("key_file: %ld  data_file: %ld",
401 		     (long) info->state->key_file_length,
402 		     (long) info->state->data_file_length));
403   DBUG_VOID_RETURN;
404 }
405 
406 
407 /*
408   Check if should allow concurrent inserts
409 
410   IMPLEMENTATION
411     Allow concurrent inserts if we don't have a hole in the table or
412     if there is no active write lock and there is active read locks and
413     myisam_concurrent_insert == 2. In this last case the new
414     row('s) are inserted at end of file instead of filling up the hole.
415 
416     The last case is to allow one to inserts into a heavily read-used table
417     even if there is holes.
418 
419   NOTES
420     If there is a an rtree indexes in the table, concurrent inserts are
421     disabled in mi_open()
422 
423   RETURN
424     0  ok to use concurrent inserts
425     1  not ok
426 */
427 
mi_check_status(void * param)428 my_bool mi_check_status(void *param)
429 {
430   MI_INFO *info=(MI_INFO*) param;
431   DBUG_ENTER("mi_check_status");
432   DBUG_PRINT("info",("dellink: %ld  r_locks: %u  w_locks: %u",
433                      (long) info->s->state.dellink, (uint) info->s->r_locks,
434                      (uint) info->s->w_locks));
435   /*
436     The test for w_locks == 1 is here because this thread has already done an
437     external lock (in other words: w_locks == 1 means no other threads has
438     a write lock)
439   */
440   DBUG_RETURN((my_bool) !(info->s->state.dellink == HA_OFFSET_ERROR ||
441                      (myisam_concurrent_insert == 2 && info->s->r_locks &&
442                       info->s->w_locks == 1)));
443 }
444 
445 
446 /**
447   Fix status for thr_lock_merge()
448 
449   @param  org_table
450   @param  new_table that should point on org_lock.  new_table is 0
451           in case this is the first occurrence of the table in the lock
452           structure.
453 */
454 
mi_fix_status(MI_INFO * org_table,MI_INFO * new_table)455 void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table)
456 {
457   DBUG_ENTER("mi_fix_status");
458   if (!new_table)
459   {
460     /* First in group. Set state as in mi_get_status() */
461     org_table->state= &org_table->save_state;
462   }
463   else
464   {
465     /* Set new_table to use state from org_table (first lock of this table) */
466     new_table->state= org_table->state;
467   }
468   DBUG_VOID_RETURN;
469 }
470 
471 
472 /****************************************************************************
473  ** functions to read / write the state
474 ****************************************************************************/
475 
_mi_readinfo(register MI_INFO * info,int lock_type,int check_keybuffer)476 int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
477 {
478   DBUG_ENTER("_mi_readinfo");
479 
480   if (info->lock_type == F_UNLCK)
481   {
482     MYISAM_SHARE *share=info->s;
483     if (!share->tot_locks)
484     {
485       if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
486 		  info->lock_wait | MY_SEEK_NOT_DONE))
487 	DBUG_RETURN(1);
488       if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
489       {
490 	int error= my_errno ? my_errno : HA_ERR_FILE_TOO_SHORT;
491 	(void) my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
492 		     MYF(MY_SEEK_NOT_DONE));
493 	my_errno= error;
494 	DBUG_RETURN(1);
495       }
496     }
497     if (check_keybuffer)
498       (void) _mi_test_if_changed(info);
499     info->invalidator=info->s->invalidator;
500   }
501   else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
502   {
503     my_errno=EACCES;				/* Not allowed to change */
504     DBUG_RETURN(-1);				/* when have read_lock() */
505   }
506   DBUG_RETURN(0);
507 } /* _mi_readinfo */
508 
509 
510 /*
511   Every isam-function that uppdates the isam-database MUST end with this
512   request
513 */
514 
_mi_writeinfo(register MI_INFO * info,uint operation)515 int _mi_writeinfo(register MI_INFO *info, uint operation)
516 {
517   int error,olderror;
518   MYISAM_SHARE *share=info->s;
519   DBUG_ENTER("_mi_writeinfo");
520   DBUG_PRINT("info",("operation: %u  tot_locks: %u", operation,
521 		     share->tot_locks));
522 
523   error=0;
524   if (share->tot_locks == 0)
525   {
526     olderror=my_errno;			/* Remember last error */
527     if (operation)
528     {					/* Two threads can't be here */
529       share->state.process= share->last_process=   share->this_process;
530       share->state.unique=  info->last_unique=	   info->this_unique;
531       share->state.update_count= info->last_loop= ++info->this_loop;
532       if ((error=mi_state_info_write(share->kfile, &share->state, 1)))
533 	olderror=my_errno;
534 #ifdef _WIN32
535       if (myisam_flush)
536       {
537         if (share->file_map)
538           my_msync(info->dfile, share->file_map, share->mmaped_length, MS_SYNC);
539         mysql_file_sync(share->kfile, 0);
540         mysql_file_sync(info->dfile, 0);
541       }
542 #endif
543     }
544     if (!(operation & WRITEINFO_NO_UNLOCK) &&
545 	my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
546 		MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
547       DBUG_RETURN(1);
548     my_errno=olderror;
549   }
550   else if (operation)
551     share->changed= 1;			/* Mark keyfile changed */
552   DBUG_RETURN(error);
553 } /* _mi_writeinfo */
554 
555 
556 	/* Test if someone has changed the database */
557 	/* (Should be called after readinfo) */
558 
_mi_test_if_changed(register MI_INFO * info)559 int _mi_test_if_changed(register MI_INFO *info)
560 {
561   MYISAM_SHARE *share=info->s;
562   if (share->state.process != share->last_process ||
563       share->state.unique  != info->last_unique ||
564       share->state.update_count != info->last_loop)
565   {						/* Keyfile has changed */
566     DBUG_PRINT("info",("index file changed"));
567     if (share->state.process != share->this_process)
568       (void) flush_key_blocks(share->key_cache, share->kfile,
569                             &share->dirty_part_map, FLUSH_RELEASE);
570     share->last_process=share->state.process;
571     info->last_unique=	share->state.unique;
572     info->last_loop=	share->state.update_count;
573     info->update|=	HA_STATE_WRITTEN;	/* Must use file on next */
574     info->data_changed= 1;			/* For mi_is_changed */
575     return 1;
576   }
577   return (!(info->update & HA_STATE_AKTIV) ||
578 	  (info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
579 			   HA_STATE_KEY_CHANGED)));
580 } /* _mi_test_if_changed */
581 
582 
583 /*
584   Put a mark in the .MYI file that someone is updating the table
585 
586 
587   DOCUMENTATION
588 
589   state.open_count in the .MYI file is used the following way:
590   - For the first change of the .MYI file in this process open_count is
591     incremented by mi_mark_file_change(). (We have a write lock on the file
592     when this happens)
593   - In mi_close() it's decremented by _mi_decrement_open_count() if it
594     was incremented in the same process.
595 
596   This mean that if we are the only process using the file, the open_count
597   tells us if the MYISAM file wasn't properly closed. (This is true if
598   my_disable_locking is set).
599 */
600 
601 
_mi_mark_file_changed(MI_INFO * info)602 int _mi_mark_file_changed(MI_INFO *info)
603 {
604   uchar buff[3];
605   register MYISAM_SHARE *share=info->s;
606   DBUG_ENTER("_mi_mark_file_changed");
607 
608   if (!(share->state.changed & STATE_CHANGED) || ! share->global_changed)
609   {
610     share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
611 			   STATE_NOT_OPTIMIZED_KEYS);
612     if (!share->global_changed)
613     {
614       share->global_changed=1;
615       share->state.open_count++;
616     }
617     if (!share->temporary)
618     {
619       mi_int2store(buff,share->state.open_count);
620       buff[2]=1;				/* Mark that it's changed */
621       DBUG_RETURN((int)mysql_file_pwrite(share->kfile, buff, sizeof(buff),
622                                     sizeof(share->state.header),
623                                     MYF(MY_NABP)));
624     }
625   }
626   DBUG_RETURN(0);
627 }
628 
629 
630 /*
631   This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
632   call.  In these context the following code should be safe!
633  */
634 
_mi_decrement_open_count(MI_INFO * info)635 int _mi_decrement_open_count(MI_INFO *info)
636 {
637   uchar buff[2];
638   register MYISAM_SHARE *share=info->s;
639   int lock_error=0,write_error=0;
640   if (share->global_changed)
641   {
642     uint old_lock=info->lock_type;
643     share->global_changed=0;
644     lock_error= my_disable_locking ? 0 : mi_lock_database(info,F_WRLCK);
645     /* Its not fatal even if we couldn't get the lock ! */
646     if (share->state.open_count > 0)
647     {
648       share->state.open_count--;
649       mi_int2store(buff,share->state.open_count);
650       write_error= (mysql_file_pwrite(share->kfile, buff, sizeof(buff),
651                                      sizeof(share->state.header),
652                                      MYF(MY_NABP)) != 0);
653     }
654     if (!lock_error && !my_disable_locking)
655       lock_error=mi_lock_database(info,old_lock);
656   }
657   return MY_TEST(lock_error || write_error);
658 }
659 
660 
_mi_report_crashed_ignore(MI_INFO * file,const char * message,const char * sfile,uint sline)661 void _mi_report_crashed_ignore(MI_INFO *file __attribute__((unused)),
662                                const char *message __attribute__((unused)),
663                                const char *sfile __attribute__((unused)),
664                                uint sline __attribute__((unused)))
665 {
666 }
667