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
22static 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
41int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
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
414void 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 */
424static void mi_extra_keyflag(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
444int 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
478my_bool mi_killed_standalone(MI_INFO *info __attribute__((unused)))
479{
480 return 0;
481}
482