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