1
/* Copyright (C) 2000-2006 MySQL AB
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.
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.
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 */
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
25
/* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
27
int mi_lock_database(MI_INFO *info, int lock_type)
31
MYISAM_SHARE *share=info->s;
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,
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)
43
if (lock_type == F_EXTRA_LCK) /* Used by TMP tables */
47
info->lock_type= lock_type;
48
info->s->in_use= list_add(info->s->in_use, &info->in_use);
53
pthread_mutex_lock(&share->intern_lock);
54
if (share->kfile >= 0) /* May only be false on windows */
58
ftparser_call_deinitializer(info);
59
if (info->lock_type == F_RDLCK)
60
count= --share->r_locks;
62
count= --share->w_locks;
64
if (info->lock_type == F_WRLCK && !share->w_locks &&
65
!share->delay_key_write && flush_key_blocks(share->key_cache,
66
share->kfile,FLUSH_KEEP))
69
mi_print_error(info->s, HA_ERR_CRASHED);
70
mi_mark_crashed(info); /* Mark that table must be checked */
72
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
74
if (end_io_cache(&info->rec_cache))
77
mi_print_error(info->s, HA_ERR_CRASHED);
78
mi_mark_crashed(info);
83
DBUG_PRINT("info",("changed: %u w_locks: %u",
84
(uint) share->changed, share->w_locks));
85
if (share->changed && !share->w_locks)
88
if ((info->s->mmaped_length != info->s->state.state.data_file_length) &&
89
(info->s->nonmmaped_inserts > MAX_NONMAPPED_INSERTS))
91
if (info->s->concurrent_insert)
92
rw_wrlock(&info->s->mmap_lock);
93
mi_remap_file(info, info->s->state.state.data_file_length);
94
info->s->nonmmaped_inserts= 0;
95
if (info->s->concurrent_insert)
96
rw_unlock(&info->s->mmap_lock);
99
share->state.process= share->last_process=share->this_process;
100
share->state.unique= info->last_unique= info->this_unique;
101
share->state.update_count= info->last_loop= ++info->this_loop;
102
if (mi_state_info_write(share->kfile, &share->state, 1))
107
if (my_sync(share->kfile, MYF(0)))
109
if (my_sync(info->dfile, MYF(0)))
113
share->not_flushed=1;
116
mi_print_error(info->s, HA_ERR_CRASHED);
117
mi_mark_crashed(info);
120
if (info->lock_type != F_EXTRA_LCK)
123
{ /* Only read locks left */
125
if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
126
MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
129
else if (!share->w_locks)
130
{ /* No more locks */
132
if (my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
133
MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
138
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
139
info->lock_type= F_UNLCK;
140
info->s->in_use= list_delete(info->s->in_use, &info->in_use);
143
if (info->lock_type == F_WRLCK)
146
Change RW to READONLY
148
mysqld does not turn write locks to read locks,
149
so we're never here in mysqld.
151
if (share->w_locks == 1)
154
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
155
MYF(MY_SEEK_NOT_DONE)))
163
info->lock_type=lock_type;
166
if (!share->r_locks && !share->w_locks)
169
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
170
info->lock_wait | MY_SEEK_NOT_DONE))
175
if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
178
VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)));
183
VOID(_mi_test_if_changed(info));
186
info->lock_type=lock_type;
187
info->s->in_use= list_add(info->s->in_use, &info->in_use);
190
if (info->lock_type == F_RDLCK)
191
{ /* Change READONLY to RW */
192
if (share->r_locks == 1)
195
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
196
MYF(info->lock_wait | MY_SEEK_NOT_DONE)))
203
info->lock_type=lock_type;
207
if (!(share->options & HA_OPTION_READ_ONLY_DATA))
212
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
213
info->lock_wait | MY_SEEK_NOT_DONE))
220
if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
223
VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
224
info->lock_wait | MY_SEEK_NOT_DONE));
231
VOID(_mi_test_if_changed(info));
233
info->lock_type=lock_type;
234
info->invalidator=info->s->invalidator;
237
info->s->in_use= list_add(info->s->in_use, &info->in_use);
240
break; /* Impossible */
247
Check for bad file descriptors if this table is part
248
of a merge union. Failing to capture this may cause
249
a crash on windows if the table is renamed and
250
later on referenced by the merge table.
252
if( info->owned_by_merge && (info->s)->kfile < 0 )
254
error = HA_ERR_NO_SUCH_TABLE;
258
pthread_mutex_unlock(&share->intern_lock);
259
#if defined(FULL_LOG) || defined(_lint)
260
lock_type|=(int) (flag << 8); /* Set bit to set if real lock */
261
myisam_log_command(MI_LOG_LOCK,info,(uchar*) &lock_type,sizeof(lock_type),
265
} /* mi_lock_database */
268
/****************************************************************************
269
The following functions are called by thr_lock() in threaded applications
270
****************************************************************************/
273
Create a copy of the current status for the table
277
param Pointer to Myisam handler
278
concurrent_insert Set to 1 if we are going to do concurrent inserts
279
(THR_WRITE_CONCURRENT_INSERT was used)
282
void mi_get_status(void* param, int concurrent_insert)
284
MI_INFO *info=(MI_INFO*) param;
285
DBUG_ENTER("mi_get_status");
286
DBUG_PRINT("info",("key_file: %ld data_file: %ld concurrent_insert: %d",
287
(long) info->s->state.state.key_file_length,
288
(long) info->s->state.state.data_file_length,
291
if (info->state->key_file_length > info->s->state.state.key_file_length ||
292
info->state->data_file_length > info->s->state.state.data_file_length)
293
DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
294
(long) info->state->key_file_length,
295
(long) info->state->data_file_length));
297
info->save_state=info->s->state.state;
298
info->state= &info->save_state;
299
info->append_insert_at_end= concurrent_insert;
304
void mi_update_status(void* param)
306
MI_INFO *info=(MI_INFO*) param;
308
Because someone may have closed the table we point at, we only
309
update the state if its our own state. This isn't a problem as
310
we are always pointing at our own lock or at a read lock.
311
(This is enforced by thr_multi_lock.c)
313
if (info->state == &info->save_state)
316
DBUG_PRINT("info",("updating status: key_file: %ld data_file: %ld",
317
(long) info->state->key_file_length,
318
(long) info->state->data_file_length));
319
if (info->state->key_file_length < info->s->state.state.key_file_length ||
320
info->state->data_file_length < info->s->state.state.data_file_length)
321
DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
322
(long) info->s->state.state.key_file_length,
323
(long) info->s->state.state.data_file_length));
325
info->s->state.state= *info->state;
326
info->state= &info->s->state.state;
328
info->append_insert_at_end= 0;
331
We have to flush the write cache here as other threads may start
332
reading the table before mi_lock_database() is called
334
if (info->opt_flag & WRITE_CACHE_USED)
336
if (end_io_cache(&info->rec_cache))
338
mi_print_error(info->s, HA_ERR_CRASHED);
339
mi_mark_crashed(info);
341
info->opt_flag&= ~WRITE_CACHE_USED;
346
void mi_restore_status(void *param)
348
MI_INFO *info= (MI_INFO*) param;
349
info->state= &info->s->state.state;
350
info->append_insert_at_end= 0;
354
void mi_copy_status(void* to,void *from)
356
((MI_INFO*) to)->state= &((MI_INFO*) from)->save_state;
361
Check if should allow concurrent inserts
364
Allow concurrent inserts if we don't have a hole in the table or
365
if there is no active write lock and there is active read locks and
366
myisam_concurrent_insert == 2. In this last case the new
367
row('s) are inserted at end of file instead of filling up the hole.
369
The last case is to allow one to inserts into a heavily read-used table
370
even if there is holes.
373
If there is a an rtree indexes in the table, concurrent inserts are
374
disabled in mi_open()
377
0 ok to use concurrent inserts
381
my_bool mi_check_status(void *param)
383
MI_INFO *info=(MI_INFO*) param;
385
The test for w_locks == 1 is here because this thread has already done an
386
external lock (in other words: w_locks == 1 means no other threads has
389
DBUG_PRINT("info",("dellink: %ld r_locks: %u w_locks: %u",
390
(long) info->s->state.dellink, (uint) info->s->r_locks,
391
(uint) info->s->w_locks));
392
return (my_bool) !(info->s->state.dellink == HA_OFFSET_ERROR ||
393
(myisam_concurrent_insert == 2 && info->s->r_locks &&
394
info->s->w_locks == 1));
398
/****************************************************************************
399
** functions to read / write the state
400
****************************************************************************/
402
int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
404
DBUG_ENTER("_mi_readinfo");
406
if (info->lock_type == F_UNLCK)
408
MYISAM_SHARE *share=info->s;
409
if (!share->tot_locks)
411
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
412
info->lock_wait | MY_SEEK_NOT_DONE))
414
if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
416
int error=my_errno ? my_errno : -1;
417
VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
418
MYF(MY_SEEK_NOT_DONE)));
424
VOID(_mi_test_if_changed(info));
425
info->invalidator=info->s->invalidator;
427
else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
429
my_errno=EACCES; /* Not allowed to change */
430
DBUG_RETURN(-1); /* when have read_lock() */
437
Every isam-function that uppdates the isam-database MUST end with this
441
int _mi_writeinfo(register MI_INFO *info, uint operation)
444
MYISAM_SHARE *share=info->s;
445
DBUG_ENTER("_mi_writeinfo");
446
DBUG_PRINT("info",("operation: %u tot_locks: %u", operation,
450
if (share->tot_locks == 0)
452
olderror=my_errno; /* Remember last error */
454
{ /* Two threads can't be here */
455
share->state.process= share->last_process= share->this_process;
456
share->state.unique= info->last_unique= info->this_unique;
457
share->state.update_count= info->last_loop= ++info->this_loop;
458
if ((error=mi_state_info_write(share->kfile, &share->state, 1)))
463
_commit(share->kfile);
464
_commit(info->dfile);
468
if (!(operation & WRITEINFO_NO_UNLOCK) &&
469
my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
470
MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
475
share->changed= 1; /* Mark keyfile changed */
477
} /* _mi_writeinfo */
480
/* Test if someone has changed the database */
481
/* (Should be called after readinfo) */
483
int _mi_test_if_changed(register MI_INFO *info)
485
MYISAM_SHARE *share=info->s;
486
if (share->state.process != share->last_process ||
487
share->state.unique != info->last_unique ||
488
share->state.update_count != info->last_loop)
489
{ /* Keyfile has changed */
490
DBUG_PRINT("info",("index file changed"));
491
if (share->state.process != share->this_process)
492
VOID(flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE));
493
share->last_process=share->state.process;
494
info->last_unique= share->state.unique;
495
info->last_loop= share->state.update_count;
496
info->update|= HA_STATE_WRITTEN; /* Must use file on next */
497
info->data_changed= 1; /* For mi_is_changed */
500
return (!(info->update & HA_STATE_AKTIV) ||
501
(info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
502
HA_STATE_KEY_CHANGED)));
503
} /* _mi_test_if_changed */
507
Put a mark in the .MYI file that someone is updating the table
512
state.open_count in the .MYI file is used the following way:
513
- For the first change of the .MYI file in this process open_count is
514
incremented by mi_mark_file_change(). (We have a write lock on the file
516
- In mi_close() it's decremented by _mi_decrement_open_count() if it
517
was incremented in the same process.
519
This mean that if we are the only process using the file, the open_count
520
tells us if the MYISAM file wasn't properly closed. (This is true if
521
my_disable_locking is set).
525
int _mi_mark_file_changed(MI_INFO *info)
528
register MYISAM_SHARE *share=info->s;
529
DBUG_ENTER("_mi_mark_file_changed");
531
if (!(share->state.changed & STATE_CHANGED) || ! share->global_changed)
533
share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
534
STATE_NOT_OPTIMIZED_KEYS);
535
if (!share->global_changed)
537
share->global_changed=1;
538
share->state.open_count++;
540
if (!share->temporary)
542
mi_int2store(buff,share->state.open_count);
543
buff[2]=1; /* Mark that it's changed */
544
DBUG_RETURN(my_pwrite(share->kfile,buff,sizeof(buff),
545
sizeof(share->state.header),
554
This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
555
call. In these context the following code should be safe!
558
int _mi_decrement_open_count(MI_INFO *info)
561
register MYISAM_SHARE *share=info->s;
562
int lock_error=0,write_error=0;
563
if (share->global_changed)
565
uint old_lock=info->lock_type;
566
share->global_changed=0;
567
lock_error=mi_lock_database(info,F_WRLCK);
568
/* Its not fatal even if we couldn't get the lock ! */
569
if (share->state.open_count > 0)
571
share->state.open_count--;
572
mi_int2store(buff,share->state.open_count);
573
write_error=my_pwrite(share->kfile,buff,sizeof(buff),
574
sizeof(share->state.header),
578
lock_error=mi_lock_database(info,old_lock);
580
return test(lock_error || write_error);