1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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       set_my_errno(EACCES);
77       break;
78     }
79     if (info->s->file_map) /* Don't use cache if mmap */
80       break;
81 #if 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     // Fall through.
160   case HA_EXTRA_NO_CACHE:
161     if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
162     {
163       info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
164       error=end_io_cache(&info->rec_cache);
165       /* Sergei will insert full text index caching here */
166     }
167 #if defined(HAVE_MADVISE)
168     if (info->opt_flag & MEMMAP_USED)
169       madvise((char*) share->file_map, share->state.state.data_file_length,
170               MADV_RANDOM);
171 #endif
172     break;
173   case HA_EXTRA_FLUSH_CACHE:
174     if (info->opt_flag & WRITE_CACHE_USED)
175     {
176       if ((error=flush_io_cache(&info->rec_cache)))
177       {
178         mi_print_error(info->s, HA_ERR_CRASHED);
179 	mi_mark_crashed(info);			/* Fatal error found */
180       }
181     }
182     break;
183   case HA_EXTRA_NO_READCHECK:
184     info->opt_flag&= ~READ_CHECK_USED;		/* No readcheck */
185     break;
186   case HA_EXTRA_READCHECK:
187     info->opt_flag|= READ_CHECK_USED;
188     break;
189   case HA_EXTRA_KEYREAD:			/* Read only keys to record */
190   case HA_EXTRA_REMEMBER_POS:
191     info->opt_flag |= REMEMBER_OLD_POS;
192     memmove((uchar*) info->lastkey + share->base.max_key_length * 2,
193             (uchar*) info->lastkey, info->lastkey_length);
194     info->save_update=	info->update;
195     info->save_lastinx= info->lastinx;
196     info->save_lastpos= info->lastpos;
197     info->save_lastkey_length=info->lastkey_length;
198     if (function == HA_EXTRA_REMEMBER_POS)
199       break;
200     /* fall through */
201   case HA_EXTRA_KEYREAD_CHANGE_POS:
202     info->opt_flag |= KEY_READ_USED;
203     info->read_record=_mi_read_key_record;
204     break;
205   case HA_EXTRA_NO_KEYREAD:
206   case HA_EXTRA_RESTORE_POS:
207     if (info->opt_flag & REMEMBER_OLD_POS)
208     {
209       memmove((uchar*) info->lastkey,
210               (uchar*) info->lastkey + share->base.max_key_length * 2,
211               info->save_lastkey_length);
212       info->update=	info->save_update | HA_STATE_WRITTEN;
213       info->lastinx=	info->save_lastinx;
214       info->lastpos=	info->save_lastpos;
215       info->lastkey_length=info->save_lastkey_length;
216     }
217     info->read_record=	share->read_record;
218     info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS);
219     break;
220   case HA_EXTRA_NO_USER_CHANGE: /* Database is somehow locked agains changes */
221     info->lock_type= F_EXTRA_LCK; /* Simulate as locked */
222     break;
223   case HA_EXTRA_WAIT_LOCK:
224     info->lock_wait=0;
225     break;
226   case HA_EXTRA_NO_WAIT_LOCK:
227     info->lock_wait=MY_DONT_WAIT;
228     break;
229   case HA_EXTRA_NO_KEYS:
230     if (info->lock_type == F_UNLCK)
231     {
232       error=1;					/* Not possibly if not lock */
233       break;
234     }
235     if (mi_is_any_key_active(share->state.key_map))
236     {
237       MI_KEYDEF *key=share->keyinfo;
238       uint i;
239       for (i=0 ; i < share->base.keys ; i++,key++)
240       {
241         if (!(key->flag & HA_NOSAME) && info->s->base.auto_key != i+1)
242         {
243           mi_clear_key_active(share->state.key_map, i);
244           info->update|= HA_STATE_CHANGED;
245         }
246       }
247 
248       if (!share->changed)
249       {
250 	share->state.changed|= STATE_CHANGED | STATE_NOT_ANALYZED;
251 	share->changed=1;			/* Update on close */
252 	if (!share->global_changed)
253 	{
254 	  share->global_changed=1;
255 	  share->state.open_count++;
256 	}
257       }
258       share->state.state= *info->state;
259       error=mi_state_info_write(share->kfile,&share->state,1 | 2);
260     }
261     break;
262   case HA_EXTRA_FORCE_REOPEN:
263     mysql_mutex_lock(&THR_LOCK_myisam);
264     share->last_version= 0L;			/* Impossible version */
265     mysql_mutex_unlock(&THR_LOCK_myisam);
266     break;
267   case HA_EXTRA_PREPARE_FOR_DROP:
268     mysql_mutex_lock(&THR_LOCK_myisam);
269     share->last_version= 0L;			/* Impossible version */
270     mysql_mutex_unlock(&THR_LOCK_myisam);
271     break;
272   case HA_EXTRA_FLUSH:
273     if (!share->temporary)
274       flush_key_blocks(share->key_cache, keycache_thread_var(),
275                        share->kfile, FLUSH_KEEP);
276 #ifndef _WIN32
277     _mi_decrement_open_count(info);
278 #endif
279     if (share->not_flushed)
280     {
281       share->not_flushed=0;
282       if (mysql_file_sync(share->kfile, MYF(0)))
283 	error= my_errno();
284       if (mysql_file_sync(info->dfile, MYF(0)))
285 	error= my_errno();
286       if (error)
287       {
288 	share->changed=1;
289         mi_print_error(info->s, HA_ERR_CRASHED);
290 	mi_mark_crashed(info);			/* Fatal error found */
291       }
292     }
293     if (share->base.blobs)
294       mi_alloc_rec_buff(info, -1, &info->rec_buff);
295     break;
296   case HA_EXTRA_NORMAL:				/* Theese isn't in use */
297     info->quick_mode=0;
298     break;
299   case HA_EXTRA_QUICK:
300     info->quick_mode=1;
301     break;
302   case HA_EXTRA_NO_ROWS:
303     if (!share->state.header.uniques)
304       info->opt_flag|= OPT_NO_ROWS;
305     break;
306   case HA_EXTRA_PRELOAD_BUFFER_SIZE:
307     info->preload_buff_size= *((ulong *) extra_arg);
308     break;
309   case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
310   case HA_EXTRA_CHANGE_KEY_TO_DUP:
311     mi_extra_keyflag(info, function);
312     break;
313   case HA_EXTRA_MMAP:
314     mysql_mutex_lock(&share->intern_lock);
315     /*
316       Memory map the data file if it is not already mapped. It is safe
317       to memory map a file while other threads are using file I/O on it.
318       Assigning a new address to a function pointer is an atomic
319       operation. intern_lock prevents that two or more mappings are done
320       at the same time.
321     */
322     if (!share->file_map)
323     {
324       if (mi_dynmap_file(info, share->state.state.data_file_length))
325       {
326         DBUG_PRINT("warning",("mmap failed: errno: %d",errno));
327         error= errno;
328         set_my_errno(error);
329       }
330     }
331     mysql_mutex_unlock(&share->intern_lock);
332     break;
333   case HA_EXTRA_MARK_AS_LOG_TABLE:
334     mysql_mutex_lock(&share->intern_lock);
335     share->is_log_table= TRUE;
336     mysql_mutex_unlock(&share->intern_lock);
337     break;
338   case HA_EXTRA_KEY_CACHE:
339   case HA_EXTRA_NO_KEY_CACHE:
340   default:
341     break;
342   }
343   {
344     char tmp[1];
345     tmp[0]=function;
346     myisam_log_command(MI_LOG_EXTRA,info,(uchar*) tmp,1,error);
347   }
348   DBUG_RETURN(error);
349 } /* mi_extra */
350 
351 
mi_set_index_cond_func(MI_INFO * info,index_cond_func_t func,void * func_arg)352 void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func,
353                             void *func_arg)
354 {
355   info->index_cond_func= func;
356   info->index_cond_func_arg= func_arg;
357 }
358 
359 /*
360     Start/Stop Inserting Duplicates Into a Table, WL#1648.
361  */
mi_extra_keyflag(MI_INFO * info,enum ha_extra_function function)362 static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function)
363 {
364   uint  idx;
365 
366   for (idx= 0; idx< info->s->base.keys; idx++)
367   {
368     switch (function) {
369     case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
370       info->s->keyinfo[idx].flag|= HA_NOSAME;
371       break;
372     case HA_EXTRA_CHANGE_KEY_TO_DUP:
373       info->s->keyinfo[idx].flag&= ~(HA_NOSAME);
374       break;
375     default:
376       break;
377     }
378   }
379 }
380 
381 
mi_reset(MI_INFO * info)382 int mi_reset(MI_INFO *info)
383 {
384   int error= 0;
385   MYISAM_SHARE *share=info->s;
386   DBUG_ENTER("mi_reset");
387   /*
388     Free buffers and reset the following flags:
389     EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK
390 
391     If the row buffer cache is large (for dynamic tables), reduce it
392     to save memory.
393   */
394   if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
395   {
396     info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
397     error= end_io_cache(&info->rec_cache);
398   }
399   if (share->base.blobs)
400     mi_alloc_rec_buff(info, -1, &info->rec_buff);
401 #if defined(HAVE_MADVISE)
402   if (info->opt_flag & MEMMAP_USED)
403     madvise((char*) share->file_map, share->state.state.data_file_length,
404             MADV_RANDOM);
405 #endif
406   info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS);
407   info->quick_mode=0;
408   info->lastinx= 0;			/* Use first index as def */
409   info->last_search_keypage= info->lastpos= HA_OFFSET_ERROR;
410   info->page_changed= 1;
411   info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
412                  HA_STATE_PREV_FOUND);
413   DBUG_RETURN(error);
414 }
415