~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to storage/myisam/mi_locking.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

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