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