1 : /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 Maria-tables.
18 : Must be first request before doing any furter calls to any Maria function.
19 : Is used to allow many process use the same non transactional Maria table
20 : */
21 :
22 : #include "ma_ftdefs.h"
23 :
24 : /* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
25 :
26 : int maria_lock_database(MARIA_HA *info, int lock_type)
27 4948 : {
28 : int error;
29 : uint count;
30 4948 : MARIA_SHARE *share= info->s;
31 4948 : DBUG_ENTER("maria_lock_database");
32 4948 : DBUG_PRINT("enter",("lock_type: %d old lock %d r_locks: %u w_locks: %u "
33 : "global_changed: %d open_count: %u name: '%s'",
34 : lock_type, info->lock_type, share->r_locks,
35 : share->w_locks,
36 : share->global_changed, share->state.open_count,
37 : share->index_file_name.str));
38 4948 : if (share->options & HA_OPTION_READ_ONLY_DATA ||
39 : info->lock_type == lock_type)
40 2342 : DBUG_RETURN(0);
41 2606 : if (lock_type == F_EXTRA_LCK) /* Used by TMP tables */
42 : {
43 629 : ++share->w_locks;
44 629 : ++share->tot_locks;
45 629 : info->lock_type= lock_type;
46 629 : DBUG_RETURN(0);
47 : }
48 :
49 1977 : error=0;
50 1977 : pthread_mutex_lock(&share->intern_lock);
51 1977 : if (share->kfile.file >= 0) /* May only be false on windows */
52 : {
53 1977 : switch (lock_type) {
54 : case F_UNLCK:
55 1297 : maria_ftparser_call_deinitializer(info);
56 1297 : if (info->lock_type == F_RDLCK)
57 : {
58 0 : count= --share->r_locks;
59 0 : if (share->lock_restore_status)
60 0 : (*share->lock_restore_status)(info);
61 : }
62 : else
63 : {
64 1297 : count= --share->w_locks;
65 1297 : if (share->lock.update_status)
66 135 : (*share->lock.update_status)(info);
67 : }
68 1297 : --share->tot_locks;
69 1297 : if (info->lock_type == F_WRLCK && !share->w_locks)
70 : {
71 : /* pages of transactional tables get flushed at Checkpoint */
72 698 : if (!share->base.born_transactional && !share->temporary &&
73 : _ma_flush_table_files(info,
74 : share->delay_key_write ? MARIA_FLUSH_DATA :
75 : MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
76 : FLUSH_KEEP, FLUSH_KEEP))
77 0 : error= my_errno;
78 : }
79 1297 : if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
80 : {
81 0 : if (end_io_cache(&info->rec_cache))
82 : {
83 0 : error=my_errno;
84 0 : maria_print_error(info->s, HA_ERR_CRASHED);
85 0 : maria_mark_crashed(info);
86 : }
87 : }
88 1297 : if (!count)
89 : {
90 1297 : DBUG_PRINT("info",("changed: %u w_locks: %u",
91 : (uint) share->changed, share->w_locks));
92 1297 : if (share->changed && !share->w_locks)
93 : {
94 : #ifdef HAVE_MMAP
95 888 : if ((share->mmaped_length !=
96 : share->state.state.data_file_length) &&
97 : (share->nonmmaped_inserts > MAX_NONMAPPED_INSERTS))
98 : {
99 0 : if (share->lock_key_trees)
100 0 : rw_wrlock(&share->mmap_lock);
101 0 : _ma_remap_file(info, share->state.state.data_file_length);
102 0 : share->nonmmaped_inserts= 0;
103 0 : if (share->lock_key_trees)
104 0 : rw_unlock(&share->mmap_lock);
105 : }
106 : #endif
107 : #ifdef EXTERNAL_LOCKING
108 : share->state.process= share->last_process=share->this_process;
109 : share->state.unique= info->last_unique= info->this_unique;
110 : share->state.update_count= info->last_loop= ++info->this_loop;
111 : #endif
112 : /* transactional tables rather flush their state at Checkpoint */
113 888 : if (!share->base.born_transactional)
114 : {
115 365 : if (_ma_state_info_write_sub(share->kfile.file, &share->state,
116 : MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET))
117 0 : error= my_errno;
118 : else
119 : {
120 : /* A value of 0 means below means "state flushed" */
121 365 : share->changed= 0;
122 : }
123 : }
124 888 : if (maria_flush)
125 : {
126 0 : if (_ma_sync_table_files(info))
127 0 : error= my_errno;
128 : }
129 : else
130 888 : share->not_flushed=1;
131 888 : if (error)
132 : {
133 0 : maria_print_error(info->s, HA_ERR_CRASHED);
134 0 : maria_mark_crashed(info);
135 : }
136 : }
137 : }
138 1297 : info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
139 1297 : info->lock_type= F_UNLCK;
140 1297 : break;
141 : case F_RDLCK:
142 0 : if (info->lock_type == F_WRLCK)
143 : {
144 : /*
145 : Change RW to READONLY
146 :
147 : mysqld does not turn write locks to read locks,
148 : so we're never here in mysqld.
149 : */
150 0 : share->w_locks--;
151 0 : share->r_locks++;
152 0 : info->lock_type=lock_type;
153 0 : break;
154 : }
155 : #ifdef MARIA_EXTERNAL_LOCKING
156 : if (!share->r_locks && !share->w_locks)
157 : {
158 : /* note that a transactional table should not do this */
159 : if (_ma_state_info_read_dsk(share->kfile.file, &share->state))
160 : {
161 : error=my_errno;
162 : break;
163 : }
164 : }
165 : #endif
166 0 : VOID(_ma_test_if_changed(info));
167 0 : share->r_locks++;
168 0 : share->tot_locks++;
169 0 : info->lock_type=lock_type;
170 0 : break;
171 : case F_WRLCK:
172 680 : if (info->lock_type == F_RDLCK)
173 : { /* Change READONLY to RW */
174 0 : if (share->r_locks == 1)
175 : {
176 0 : share->r_locks--;
177 0 : share->w_locks++;
178 0 : info->lock_type=lock_type;
179 0 : break;
180 : }
181 : }
182 : #ifdef MARIA_EXTERNAL_LOCKING
183 : if (!(share->options & HA_OPTION_READ_ONLY_DATA))
184 : {
185 : if (!share->w_locks)
186 : {
187 : if (!share->r_locks)
188 : {
189 : /*
190 : Note that transactional tables should not do this.
191 : If we enabled this code, we should make sure to skip it if
192 : born_transactional is true. We should not test
193 : now_transactional to decide if we can call
194 : _ma_state_info_read_dsk(), because it can temporarily be 0
195 : (TRUNCATE on a partitioned table) and thus it would make a state
196 : modification below without mutex, confusing a concurrent
197 : checkpoint running.
198 : Even if this code was enabled only for non-transactional tables:
199 : in scenario LOCK TABLE t1 WRITE; INSERT INTO t1; DELETE FROM t1;
200 : state on disk read by DELETE is obsolete as it was not flushed
201 : at the end of INSERT. MyISAM same. It however causes no issue as
202 : maria_delete_all_rows() calls _ma_reset_status() thus is not
203 : influenced by the obsolete read values.
204 : */
205 : if (_ma_state_info_read_dsk(share->kfile.file, &share->state))
206 : {
207 : error=my_errno;
208 : break;
209 : }
210 : }
211 : }
212 : }
213 : #endif /* defined(MARIA_EXTERNAL_LOCKING) */
214 680 : VOID(_ma_test_if_changed(info));
215 :
216 680 : info->lock_type=lock_type;
217 680 : info->invalidator=share->invalidator;
218 680 : share->w_locks++;
219 680 : share->tot_locks++;
220 680 : break;
221 : default:
222 0 : DBUG_ASSERT(0);
223 : break; /* Impossible */
224 : }
225 : }
226 : #ifdef __WIN__
227 : else
228 : {
229 : /*
230 : Check for bad file descriptors if this table is part
231 : of a merge union. Failing to capture this may cause
232 : a crash on windows if the table is renamed and
233 : later on referenced by the merge table.
234 : */
235 : if( info->owned_by_merge && (info->s)->kfile.file < 0 )
236 : {
237 : error = HA_ERR_NO_SUCH_TABLE;
238 : }
239 : }
240 : #endif
241 1977 : pthread_mutex_unlock(&share->intern_lock);
242 1977 : DBUG_RETURN(error);
243 : } /* maria_lock_database */
244 :
245 :
246 : /****************************************************************************
247 : ** functions to read / write the state
248 : ****************************************************************************/
249 :
250 : int _ma_readinfo(register MARIA_HA *info __attribute__ ((unused)),
251 : int lock_type __attribute__ ((unused)),
252 : int check_keybuffer __attribute__ ((unused)))
253 675424 : {
254 : #ifdef MARIA_EXTERNAL_LOCKING
255 : DBUG_ENTER("_ma_readinfo");
256 :
257 : if (info->lock_type == F_UNLCK)
258 : {
259 : MARIA_SHARE *share= info->s;
260 : if (!share->tot_locks)
261 : {
262 : /* should not be done for transactional tables */
263 : if (_ma_state_info_read_dsk(share->kfile.file, &share->state))
264 : {
265 : if (!my_errno)
266 : my_errno= HA_ERR_FILE_TOO_SHORT;
267 : DBUG_RETURN(1);
268 : }
269 : }
270 : if (check_keybuffer)
271 : VOID(_ma_test_if_changed(info));
272 : info->invalidator=share->invalidator;
273 : }
274 : else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
275 : {
276 : my_errno=EACCES; /* Not allowed to change */
277 : DBUG_RETURN(-1); /* when have read_lock() */
278 : }
279 : DBUG_RETURN(0);
280 : #else
281 675424 : return 0;
282 : #endif /* defined(MARIA_EXTERNAL_LOCKING) */
283 : } /* _ma_readinfo */
284 :
285 :
286 : /*
287 : Every isam-function that uppdates the isam-database MUST end with this
288 : request
289 :
290 : NOTES
291 : my_errno is not changed if this succeeds!
292 : */
293 :
294 : int _ma_writeinfo(register MARIA_HA *info, uint operation)
295 4725382 : {
296 : int error,olderror;
297 4725382 : MARIA_SHARE *share= info->s;
298 4725382 : DBUG_ENTER("_ma_writeinfo");
299 4725382 : DBUG_PRINT("info",("operation: %u tot_locks: %u", operation,
300 : share->tot_locks));
301 :
302 4725382 : error=0;
303 4725382 : if (share->tot_locks == 0 && !share->base.born_transactional)
304 : {
305 : /* transactional tables flush their state at Checkpoint */
306 262068 : if (operation)
307 : { /* Two threads can't be here */
308 126849 : olderror= my_errno; /* Remember last error */
309 :
310 : #ifdef EXTERNAL_LOCKING
311 : /*
312 : The following only makes sense if we want to be allow two different
313 : processes access the same table at the same time
314 : */
315 : share->state.process= share->last_process= share->this_process;
316 : share->state.unique= info->last_unique= info->this_unique;
317 : share->state.update_count= info->last_loop= ++info->this_loop;
318 : #endif
319 :
320 126849 : if ((error=
321 : _ma_state_info_write_sub(share->kfile.file,
322 : &share->state,
323 : MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET)))
324 0 : olderror=my_errno;
325 : #ifdef __WIN__
326 : if (maria_flush)
327 : {
328 : _commit(share->kfile.file);
329 : _commit(info->dfile.file);
330 : }
331 : #endif
332 126849 : my_errno=olderror;
333 : }
334 : }
335 4463314 : else if (operation)
336 4462334 : share->changed= 1; /* Mark keyfile changed */
337 4725382 : DBUG_RETURN(error);
338 : } /* _ma_writeinfo */
339 :
340 :
341 : /*
342 : Test if an external process has changed the database
343 : (Should be called after readinfo)
344 : */
345 :
346 : int _ma_test_if_changed(register MARIA_HA *info)
347 203183 : {
348 : #ifdef EXTERNAL_LOCKING
349 : MARIA_SHARE *share= info->s;
350 : if (share->state.process != share->last_process ||
351 : share->state.unique != info->last_unique ||
352 : share->state.update_count != info->last_loop)
353 : { /* Keyfile has changed */
354 : DBUG_PRINT("info",("index file changed"));
355 : if (share->state.process != share->this_process)
356 : VOID(flush_pagecache_blocks(share->pagecache, &share->kfile,
357 : FLUSH_RELEASE));
358 : share->last_process=share->state.process;
359 : info->last_unique= share->state.unique;
360 : info->last_loop= share->state.update_count;
361 : info->update|= HA_STATE_WRITTEN; /* Must use file on next */
362 : info->data_changed= 1; /* For maria_is_changed */
363 : return 1;
364 : }
365 : #endif
366 203183 : return (!(info->update & HA_STATE_AKTIV) ||
367 : (info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
368 : HA_STATE_KEY_CHANGED)));
369 : } /* _ma_test_if_changed */
370 :
371 :
372 : /*
373 : Put a mark in the .MAI file that someone is updating the table
374 :
375 : DOCUMENTATION
376 : state.open_count in the .MAI file is used the following way:
377 : - For the first change of the .MYI file in this process open_count is
378 : incremented by _ma_mark_file_changed(). (We have a write lock on the file
379 : when this happens)
380 : - In maria_close() it's decremented by _ma_decrement_open_count() if it
381 : was incremented in the same process.
382 :
383 : This mean that if we are the only process using the file, the open_count
384 : tells us if the MARIA file wasn't properly closed. (This is true if
385 : my_disable_locking is set).
386 :
387 : open_count is not maintained on disk for temporary tables.
388 : */
389 :
390 : int _ma_mark_file_changed(MARIA_HA *info)
391 509255 : {
392 : uchar buff[3];
393 509255 : register MARIA_SHARE *share= info->s;
394 509255 : int error= 1;
395 509255 : DBUG_ENTER("_ma_mark_file_changed");
396 :
397 : #define _MA_ALREADY_MARKED_FILE_CHANGED \
398 : ((share->state.changed & STATE_CHANGED) && share->global_changed)
399 509255 : if (_MA_ALREADY_MARKED_FILE_CHANGED)
400 508535 : DBUG_RETURN(0);
401 720 : pthread_mutex_lock(&share->intern_lock); /* recheck under mutex */
402 720 : if (! _MA_ALREADY_MARKED_FILE_CHANGED)
403 : {
404 720 : share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
405 : STATE_NOT_OPTIMIZED_KEYS);
406 720 : if (!share->global_changed)
407 : {
408 720 : share->global_changed=1;
409 720 : share->state.open_count++;
410 : }
411 : /*
412 : Temp tables don't need an open_count as they are removed on crash.
413 : In theory transactional tables are fixed by log-based recovery, so don't
414 : need an open_count either, but if recovery has failed and logs have been
415 : removed (by maria-force-start-after-recovery-failures), we still need to
416 : detect dubious tables.
417 : If we didn't maintain open_count on disk for a table, after a crash
418 : we wouldn't know if it was closed at crash time (thus does not need a
419 : check) or not. So we would have to check all tables: overkill.
420 : */
421 720 : if (!share->temporary)
422 : {
423 665 : mi_int2store(buff,share->state.open_count);
424 665 : buff[2]=1; /* Mark that it's changed */
425 665 : if (my_pwrite(share->kfile.file, buff, sizeof(buff),
426 : sizeof(share->state.header) +
427 : MARIA_FILE_OPEN_COUNT_OFFSET,
428 : MYF(MY_NABP)))
429 720 : goto err;
430 : }
431 : /* Set uuid of file if not yet set (zerofilled file) */
432 720 : if (share->base.born_transactional &&
433 : !(share->state.changed & STATE_NOT_MOVABLE))
434 : {
435 : /* Lock table to current installation */
436 280 : if (_ma_set_uuid(info, 0) ||
437 : (share->state.create_rename_lsn == LSN_NEEDS_NEW_STATE_LSNS &&
438 : _ma_update_state_lsns_sub(share, LSN_IMPOSSIBLE,
439 : trnman_get_min_trid(),
440 : TRUE, TRUE)))
441 : goto err;
442 280 : share->state.changed|= STATE_NOT_MOVABLE;
443 : }
444 : }
445 720 : error= 0;
446 720 : err:
447 720 : pthread_mutex_unlock(&share->intern_lock);
448 720 : DBUG_RETURN(error);
449 : #undef _MA_ALREADY_MARKED_FILE_CHANGED
450 : }
451 :
452 : /*
453 : Check that a region is all zero
454 :
455 : SYNOPSIS
456 : check_if_zero()
457 : pos Start of memory to check
458 : length length of memory region
459 :
460 : NOTES
461 : Used mainly to detect rows with wrong extent information
462 : */
463 :
464 : my_bool _ma_check_if_zero(uchar *pos, size_t length)
465 511 : {
466 : uchar *end;
467 4185808 : for (end= pos+ length; pos != end ; pos++)
468 4185297 : if (pos[0] != 0)
469 0 : return 1;
470 511 : return 0;
471 : }
472 :
473 : /*
474 : This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
475 : call. In these context the following code should be safe!
476 : */
477 :
478 : int _ma_decrement_open_count(MARIA_HA *info)
479 3476 : {
480 : uchar buff[2];
481 3476 : register MARIA_SHARE *share= info->s;
482 3476 : int lock_error=0,write_error=0;
483 3476 : if (share->global_changed)
484 : {
485 619 : uint old_lock=info->lock_type;
486 619 : share->global_changed=0;
487 619 : lock_error=maria_lock_database(info,F_WRLCK);
488 : /* Its not fatal even if we couldn't get the lock ! */
489 619 : if (share->state.open_count > 0)
490 : {
491 619 : share->state.open_count--;
492 619 : share->changed= 1; /* We have to update state */
493 619 : if (!share->temporary)
494 : {
495 566 : mi_int2store(buff,share->state.open_count);
496 566 : write_error= (int) my_pwrite(share->kfile.file, buff, sizeof(buff),
497 : sizeof(share->state.header) +
498 : MARIA_FILE_OPEN_COUNT_OFFSET,
499 : MYF(MY_NABP));
500 : }
501 : }
502 619 : if (!lock_error)
503 619 : lock_error=maria_lock_database(info,old_lock);
504 : }
505 3476 : return test(lock_error || write_error);
506 : }
507 :
508 :
509 : /** @brief mark file as crashed */
510 :
511 : void _ma_mark_file_crashed(MARIA_SHARE *share)
512 0 : {
513 : uchar buff[2];
514 0 : DBUG_ENTER("_ma_mark_file_crashed");
515 :
516 0 : share->state.changed|= STATE_CRASHED;
517 0 : mi_int2store(buff, share->state.changed);
518 : /*
519 : We can ignore the errors, as if the mark failed, there isn't anything
520 : else we can do; The user should already have got an error that the
521 : table was crashed.
522 : */
523 0 : (void) my_pwrite(share->kfile.file, buff, sizeof(buff),
524 : sizeof(share->state.header) +
525 : MARIA_FILE_CHANGED_OFFSET,
526 : MYF(MY_NABP));
527 0 : DBUG_VOID_RETURN;
528 : }
529 :
530 :
531 : /**
532 : @brief Set uuid of for a Maria file
533 :
534 : @fn _ma_set_uuid()
535 : @param info Maria handler
536 : @param reset_uuid Instead of setting file to maria_uuid, set it to
537 : 0 to mark it as movable
538 : */
539 :
540 : my_bool _ma_set_uuid(MARIA_HA *info, my_bool reset_uuid)
541 434 : {
542 : uchar buff[MY_UUID_SIZE], *uuid;
543 :
544 434 : uuid= maria_uuid;
545 434 : if (reset_uuid)
546 : {
547 0 : bzero(buff, sizeof(buff));
548 0 : uuid= buff;
549 : }
550 434 : return (my_bool) my_pwrite(info->s->kfile.file, uuid, MY_UUID_SIZE,
551 : mi_uint2korr(info->s->state.header.base_pos),
552 : MYF(MY_NABP));
553 : }
|