1 | /* |
2 | Copyright (c) 2000, 2010, Oracle and/or its affiliates |
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-1301 USA */ |
16 | |
17 | #include "myisamdef.h" |
18 | #ifdef HAVE_SYS_MMAN_H |
19 | #include <sys/mman.h> |
20 | #endif |
21 | |
22 | static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function); |
23 | |
24 | |
25 | /* |
26 | Set options and buffers to optimize table handling |
27 | |
28 | SYNOPSIS |
29 | mi_extra() |
30 | info open table |
31 | function operation |
32 | extra_arg Pointer to extra argument (normally pointer to ulong) |
33 | Used when function is one of: |
34 | HA_EXTRA_WRITE_CACHE |
35 | HA_EXTRA_CACHE |
36 | RETURN VALUES |
37 | 0 ok |
38 | # error |
39 | */ |
40 | |
41 | int (MI_INFO *info, enum ha_extra_function function, void *) |
42 | { |
43 | int error=0; |
44 | ulong cache_size; |
45 | MYISAM_SHARE *share=info->s; |
46 | DBUG_ENTER("mi_extra" ); |
47 | DBUG_PRINT("enter" ,("function: %d" ,(int) function)); |
48 | |
49 | switch (function) { |
50 | case HA_EXTRA_RESET_STATE: /* Reset state (don't free buffers) */ |
51 | info->lastinx= 0; /* Use first index as def */ |
52 | info->last_search_keypage=info->lastpos= HA_OFFSET_ERROR; |
53 | info->page_changed=1; |
54 | /* Next/prev gives first/last */ |
55 | if (info->opt_flag & READ_CACHE_USED) |
56 | { |
57 | reinit_io_cache(&info->rec_cache,READ_CACHE,0, |
58 | (pbool) (info->lock_type != F_UNLCK), |
59 | (pbool) MY_TEST(info->update & HA_STATE_ROW_CHANGED) |
60 | ); |
61 | } |
62 | info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND | |
63 | HA_STATE_PREV_FOUND); |
64 | break; |
65 | case HA_EXTRA_CACHE: |
66 | if (info->lock_type == F_UNLCK && |
67 | (share->options & HA_OPTION_PACK_RECORD)) |
68 | { |
69 | error=1; /* Not possibly if not locked */ |
70 | my_errno=EACCES; |
71 | break; |
72 | } |
73 | if (info->s->file_map) /* Don't use cache if mmap */ |
74 | break; |
75 | #if defined(HAVE_MMAP) && defined(HAVE_MADVISE) |
76 | if ((share->options & HA_OPTION_COMPRESS_RECORD)) |
77 | { |
78 | mysql_mutex_lock(&share->intern_lock); |
79 | if (_mi_memmap_file(info)) |
80 | { |
81 | /* We don't nead MADV_SEQUENTIAL if small file */ |
82 | madvise((char*) share->file_map, share->state.state.data_file_length, |
83 | share->state.state.data_file_length <= RECORD_CACHE_SIZE*16 ? |
84 | MADV_RANDOM : MADV_SEQUENTIAL); |
85 | mysql_mutex_unlock(&share->intern_lock); |
86 | break; |
87 | } |
88 | mysql_mutex_unlock(&share->intern_lock); |
89 | } |
90 | #endif |
91 | if (info->opt_flag & WRITE_CACHE_USED) |
92 | { |
93 | info->opt_flag&= ~WRITE_CACHE_USED; |
94 | if ((error=end_io_cache(&info->rec_cache))) |
95 | break; |
96 | } |
97 | if (!(info->opt_flag & |
98 | (READ_CACHE_USED | WRITE_CACHE_USED | MEMMAP_USED))) |
99 | { |
100 | cache_size= (extra_arg ? *(ulong*) extra_arg : |
101 | my_default_record_cache_size); |
102 | if (!(init_io_cache(&info->rec_cache,info->dfile, |
103 | (uint) MY_MIN(info->state->data_file_length+1, |
104 | cache_size), |
105 | READ_CACHE,0L,(pbool) (info->lock_type != F_UNLCK), |
106 | MYF(share->write_flag & MY_WAIT_IF_FULL)))) |
107 | { |
108 | info->opt_flag|=READ_CACHE_USED; |
109 | info->update&= ~HA_STATE_ROW_CHANGED; |
110 | } |
111 | if (share->concurrent_insert) |
112 | info->rec_cache.end_of_file=info->state->data_file_length; |
113 | } |
114 | break; |
115 | case HA_EXTRA_REINIT_CACHE: |
116 | if (info->opt_flag & READ_CACHE_USED) |
117 | { |
118 | reinit_io_cache(&info->rec_cache,READ_CACHE,info->nextpos, |
119 | (pbool) (info->lock_type != F_UNLCK), |
120 | (pbool) MY_TEST(info->update & HA_STATE_ROW_CHANGED)); |
121 | info->update&= ~HA_STATE_ROW_CHANGED; |
122 | if (share->concurrent_insert) |
123 | info->rec_cache.end_of_file=info->state->data_file_length; |
124 | } |
125 | break; |
126 | case HA_EXTRA_WRITE_CACHE: |
127 | if (info->lock_type == F_UNLCK) |
128 | { |
129 | error=1; /* Not possibly if not locked */ |
130 | break; |
131 | } |
132 | |
133 | cache_size= (extra_arg ? *(ulong*) extra_arg : |
134 | my_default_record_cache_size); |
135 | if (!(info->opt_flag & |
136 | (READ_CACHE_USED | WRITE_CACHE_USED | OPT_NO_ROWS)) && |
137 | !share->state.header.uniques) |
138 | if (!(init_io_cache(&info->rec_cache,info->dfile, cache_size, |
139 | WRITE_CACHE,info->state->data_file_length, |
140 | (pbool) (info->lock_type != F_UNLCK), |
141 | MYF(share->write_flag & MY_WAIT_IF_FULL)))) |
142 | { |
143 | info->opt_flag|=WRITE_CACHE_USED; |
144 | info->update&= ~(HA_STATE_ROW_CHANGED | |
145 | HA_STATE_WRITE_AT_END | |
146 | HA_STATE_EXTEND_BLOCK); |
147 | } |
148 | break; |
149 | case HA_EXTRA_PREPARE_FOR_UPDATE: |
150 | if (info->s->data_file_type != DYNAMIC_RECORD) |
151 | break; |
152 | /* Remove read/write cache if dynamic rows */ |
153 | /* fall through */ |
154 | case HA_EXTRA_NO_CACHE: |
155 | if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) |
156 | { |
157 | info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); |
158 | error=end_io_cache(&info->rec_cache); |
159 | /* Sergei will insert full text index caching here */ |
160 | } |
161 | #if defined(HAVE_MMAP) && defined(HAVE_MADVISE) |
162 | if (info->opt_flag & MEMMAP_USED) |
163 | madvise((char*) share->file_map, share->state.state.data_file_length, |
164 | MADV_RANDOM); |
165 | #endif |
166 | break; |
167 | case HA_EXTRA_FLUSH_CACHE: |
168 | if (info->opt_flag & WRITE_CACHE_USED) |
169 | { |
170 | if ((error=flush_io_cache(&info->rec_cache))) |
171 | { |
172 | mi_print_error(info->s, HA_ERR_CRASHED); |
173 | mi_mark_crashed(info); /* Fatal error found */ |
174 | } |
175 | } |
176 | break; |
177 | case HA_EXTRA_NO_READCHECK: |
178 | info->opt_flag&= ~READ_CHECK_USED; /* No readcheck */ |
179 | break; |
180 | case HA_EXTRA_READCHECK: |
181 | info->opt_flag|= READ_CHECK_USED; |
182 | break; |
183 | case HA_EXTRA_KEYREAD: /* Read only keys to record */ |
184 | case HA_EXTRA_REMEMBER_POS: |
185 | info->opt_flag |= REMEMBER_OLD_POS; |
186 | bmove((uchar*) info->lastkey+share->base.max_key_length*2, |
187 | (uchar*) info->lastkey,info->lastkey_length); |
188 | info->save_update= info->update; |
189 | info->save_lastinx= info->lastinx; |
190 | info->save_lastpos= info->lastpos; |
191 | info->save_lastkey_length=info->lastkey_length; |
192 | if (function == HA_EXTRA_REMEMBER_POS) |
193 | break; |
194 | /* fall through */ |
195 | case HA_EXTRA_KEYREAD_CHANGE_POS: |
196 | info->opt_flag |= KEY_READ_USED; |
197 | info->read_record=_mi_read_key_record; |
198 | break; |
199 | case HA_EXTRA_NO_KEYREAD: |
200 | case HA_EXTRA_RESTORE_POS: |
201 | if (info->opt_flag & REMEMBER_OLD_POS) |
202 | { |
203 | bmove((uchar*) info->lastkey, |
204 | (uchar*) info->lastkey+share->base.max_key_length*2, |
205 | info->save_lastkey_length); |
206 | info->update= info->save_update | HA_STATE_WRITTEN; |
207 | info->lastinx= info->save_lastinx; |
208 | info->lastpos= info->save_lastpos; |
209 | info->lastkey_length=info->save_lastkey_length; |
210 | } |
211 | info->read_record= share->read_record; |
212 | info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS); |
213 | break; |
214 | case HA_EXTRA_NO_USER_CHANGE: /* Database is somehow locked agains changes */ |
215 | info->lock_type= F_EXTRA_LCK; /* Simulate as locked */ |
216 | break; |
217 | case HA_EXTRA_WAIT_LOCK: |
218 | info->lock_wait=0; |
219 | break; |
220 | case HA_EXTRA_NO_WAIT_LOCK: |
221 | info->lock_wait= MY_SHORT_WAIT; |
222 | break; |
223 | case HA_EXTRA_NO_KEYS: |
224 | if (info->lock_type == F_UNLCK) |
225 | { |
226 | error=1; /* Not possibly if not lock */ |
227 | break; |
228 | } |
229 | if (mi_is_any_key_active(share->state.key_map)) |
230 | { |
231 | MI_KEYDEF *key=share->keyinfo; |
232 | uint i; |
233 | for (i=0 ; i < share->base.keys ; i++,key++) |
234 | { |
235 | if (!(key->flag & HA_NOSAME) && info->s->base.auto_key != i+1) |
236 | { |
237 | mi_clear_key_active(share->state.key_map, i); |
238 | info->update|= HA_STATE_CHANGED; |
239 | } |
240 | } |
241 | |
242 | if (!share->changed) |
243 | { |
244 | share->state.changed|= STATE_CHANGED | STATE_NOT_ANALYZED; |
245 | share->changed=1; /* Update on close */ |
246 | if (!share->global_changed) |
247 | { |
248 | share->global_changed=1; |
249 | share->state.open_count++; |
250 | } |
251 | } |
252 | share->state.state= *info->state; |
253 | error=mi_state_info_write(share->kfile,&share->state,1 | 2); |
254 | } |
255 | break; |
256 | case HA_EXTRA_FORCE_REOPEN: |
257 | mysql_mutex_lock(&THR_LOCK_myisam); |
258 | share->last_version= 0L; /* Impossible version */ |
259 | mysql_mutex_unlock(&THR_LOCK_myisam); |
260 | break; |
261 | case HA_EXTRA_PREPARE_FOR_DROP: |
262 | /* Signals about intent to delete this table */ |
263 | share->deleting= TRUE; |
264 | share->global_changed= FALSE; /* force writing changed flag */ |
265 | _mi_mark_file_changed(info); |
266 | /* fall through */ |
267 | case HA_EXTRA_PREPARE_FOR_RENAME: |
268 | DBUG_ASSERT(!share->temporary); |
269 | mysql_mutex_lock(&THR_LOCK_myisam); |
270 | share->last_version= 0L; /* Impossible version */ |
271 | mysql_mutex_lock(&share->intern_lock); |
272 | /* Flush pages that we don't need anymore */ |
273 | if (flush_key_blocks(share->key_cache, share->kfile, |
274 | &share->dirty_part_map, |
275 | (function == HA_EXTRA_PREPARE_FOR_DROP ? |
276 | FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))) |
277 | { |
278 | error=my_errno; |
279 | share->changed=1; |
280 | mi_print_error(info->s, HA_ERR_CRASHED); |
281 | mi_mark_crashed(info); /* Fatal error found */ |
282 | } |
283 | #ifdef __WIN__REMOVE_OBSOLETE_WORKAROUND |
284 | /* Close the isam and data files as Win32 can't drop an open table */ |
285 | if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) |
286 | { |
287 | info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); |
288 | error=end_io_cache(&info->rec_cache); |
289 | } |
290 | if (info->lock_type != F_UNLCK && ! info->was_locked) |
291 | { |
292 | info->was_locked=info->lock_type; |
293 | if (mi_lock_database(info,F_UNLCK)) |
294 | error=my_errno; |
295 | info->lock_type = F_UNLCK; |
296 | } |
297 | if (share->kfile >= 0) |
298 | { |
299 | /* |
300 | We don't need to call _mi_decrement_open_count() if we are |
301 | dropping the table, as the files will be removed anyway. If we |
302 | are aborted before the files is removed, it's better to not |
303 | call it as in that case the automatic repair on open will add |
304 | the missing index entries |
305 | */ |
306 | if (function != HA_EXTRA_PREPARE_FOR_DROP) |
307 | _mi_decrement_open_count(info); |
308 | if (mysql_file_close(share->kfile,MYF(0))) |
309 | error=my_errno; |
310 | } |
311 | { |
312 | LIST *list_element ; |
313 | for (list_element=myisam_open_list ; |
314 | list_element ; |
315 | list_element=list_element->next) |
316 | { |
317 | MI_INFO *tmpinfo=(MI_INFO*) list_element->data; |
318 | if (tmpinfo->s == info->s) |
319 | { |
320 | if (tmpinfo->dfile >= 0 && mysql_file_close(tmpinfo->dfile, MYF(0))) |
321 | error = my_errno; |
322 | tmpinfo->dfile= -1; |
323 | } |
324 | } |
325 | } |
326 | share->kfile= -1; /* Files aren't open anymore */ |
327 | #endif |
328 | mysql_mutex_unlock(&share->intern_lock); |
329 | mysql_mutex_unlock(&THR_LOCK_myisam); |
330 | break; |
331 | case HA_EXTRA_FLUSH: |
332 | if (!share->temporary) |
333 | flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map, |
334 | FLUSH_KEEP); |
335 | _mi_decrement_open_count(info); |
336 | if (share->not_flushed) |
337 | { |
338 | share->not_flushed=0; |
339 | if (mysql_file_sync(share->kfile, MYF(0))) |
340 | error= my_errno; |
341 | if (mysql_file_sync(info->dfile, MYF(0))) |
342 | error= my_errno; |
343 | if (error) |
344 | { |
345 | share->changed=1; |
346 | mi_print_error(info->s, HA_ERR_CRASHED); |
347 | mi_mark_crashed(info); /* Fatal error found */ |
348 | } |
349 | } |
350 | if (share->base.blobs) |
351 | mi_alloc_rec_buff(info, -1, &info->rec_buff); |
352 | break; |
353 | case HA_EXTRA_NORMAL: /* Theese isn't in use */ |
354 | info->quick_mode=0; |
355 | break; |
356 | case HA_EXTRA_QUICK: |
357 | info->quick_mode=1; |
358 | break; |
359 | case HA_EXTRA_NO_ROWS: |
360 | if (!share->state.header.uniques) |
361 | info->opt_flag|= OPT_NO_ROWS; |
362 | break; |
363 | case HA_EXTRA_PRELOAD_BUFFER_SIZE: |
364 | info->preload_buff_size= *((ulong *) extra_arg); |
365 | break; |
366 | case HA_EXTRA_CHANGE_KEY_TO_UNIQUE: |
367 | case HA_EXTRA_CHANGE_KEY_TO_DUP: |
368 | mi_extra_keyflag(info, function); |
369 | break; |
370 | case HA_EXTRA_MMAP: |
371 | #ifdef HAVE_MMAP |
372 | mysql_mutex_lock(&share->intern_lock); |
373 | /* |
374 | Memory map the data file if it is not already mapped. It is safe |
375 | to memory map a file while other threads are using file I/O on it. |
376 | Assigning a new address to a function pointer is an atomic |
377 | operation. intern_lock prevents that two or more mappings are done |
378 | at the same time. |
379 | */ |
380 | if (!share->file_map) |
381 | { |
382 | if (mi_dynmap_file(info, share->state.state.data_file_length)) |
383 | { |
384 | DBUG_PRINT("warning" ,("mmap failed: errno: %d" ,errno)); |
385 | error= my_errno= errno; |
386 | } |
387 | } |
388 | mysql_mutex_unlock(&share->intern_lock); |
389 | #endif |
390 | break; |
391 | case HA_EXTRA_MARK_AS_LOG_TABLE: |
392 | mysql_mutex_lock(&share->intern_lock); |
393 | share->is_log_table= TRUE; |
394 | mysql_mutex_unlock(&share->intern_lock); |
395 | break; |
396 | case HA_EXTRA_DETACH_CHILD: /* When used with MERGE tables */ |
397 | info->open_flag&= ~HA_OPEN_MERGE_TABLE; |
398 | info->lock.priority&= ~THR_LOCK_MERGE_PRIV; |
399 | break; |
400 | |
401 | case HA_EXTRA_KEY_CACHE: |
402 | case HA_EXTRA_NO_KEY_CACHE: |
403 | default: |
404 | break; |
405 | } |
406 | { |
407 | char tmp[1]; |
408 | tmp[0]=function; |
409 | myisam_log_command(MI_LOG_EXTRA,info,(uchar*) tmp,1,error); |
410 | } |
411 | DBUG_RETURN(error); |
412 | } /* mi_extra */ |
413 | |
414 | void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func, |
415 | void *func_arg) |
416 | { |
417 | info->index_cond_func= func; |
418 | info->index_cond_func_arg= func_arg; |
419 | } |
420 | |
421 | /* |
422 | Start/Stop Inserting Duplicates Into a Table, WL#1648. |
423 | */ |
424 | static void (MI_INFO *info, enum ha_extra_function function) |
425 | { |
426 | uint idx; |
427 | |
428 | for (idx= 0; idx< info->s->base.keys; idx++) |
429 | { |
430 | switch (function) { |
431 | case HA_EXTRA_CHANGE_KEY_TO_UNIQUE: |
432 | info->s->keyinfo[idx].flag|= HA_NOSAME; |
433 | break; |
434 | case HA_EXTRA_CHANGE_KEY_TO_DUP: |
435 | info->s->keyinfo[idx].flag&= ~(HA_NOSAME); |
436 | break; |
437 | default: |
438 | break; |
439 | } |
440 | } |
441 | } |
442 | |
443 | |
444 | int mi_reset(MI_INFO *info) |
445 | { |
446 | int error= 0; |
447 | MYISAM_SHARE *share=info->s; |
448 | DBUG_ENTER("mi_reset" ); |
449 | /* |
450 | Free buffers and reset the following flags: |
451 | EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK |
452 | |
453 | If the row buffer cache is large (for dynamic tables), reduce it |
454 | to save memory. |
455 | */ |
456 | if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) |
457 | { |
458 | info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); |
459 | error= end_io_cache(&info->rec_cache); |
460 | } |
461 | if (share->base.blobs) |
462 | mi_alloc_rec_buff(info, -1, &info->rec_buff); |
463 | #if defined(HAVE_MMAP) && defined(HAVE_MADVISE) |
464 | if (info->opt_flag & MEMMAP_USED) |
465 | madvise((char*) share->file_map, share->state.state.data_file_length, |
466 | MADV_RANDOM); |
467 | #endif |
468 | info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS); |
469 | info->quick_mode=0; |
470 | info->lastinx= 0; /* Use first index as def */ |
471 | info->last_search_keypage= info->lastpos= HA_OFFSET_ERROR; |
472 | info->page_changed= 1; |
473 | info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND | |
474 | HA_STATE_PREV_FOUND); |
475 | DBUG_RETURN(error); |
476 | } |
477 | |
478 | my_bool mi_killed_standalone(MI_INFO *info __attribute__((unused))) |
479 | { |
480 | return 0; |
481 | } |
482 | |