~drizzle-trunk/drizzle/development

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