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