1 /* Copyright (c) 2000, 2019, 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 "my_config.h"
24 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 
29 #include "my_dbug.h"
30 #include "my_inttypes.h"
31 #include "my_macros.h"
32 #include "storage/myisam/myisamdef.h"
33 #ifdef HAVE_SYS_MMAN_H
34 #include <sys/mman.h>
35 #endif
36 
37 static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function);
38 
39 /*
40   Set options and buffers to optimize table handling
41 
42   SYNOPSIS
43     mi_extra()
44     info	open table
45     function	operation
46     extra_arg	Pointer to extra argument (normally pointer to ulong)
47                 Not used.
48   RETURN VALUES
49     0  ok
50     #  error
51 */
52 
mi_extra(MI_INFO * info,enum ha_extra_function function,void * extra_arg)53 int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) {
54   int error = 0;
55   MYISAM_SHARE *share = info->s;
56   DBUG_TRACE;
57   DBUG_PRINT("enter", ("function: %d", (int)function));
58 
59   switch (function) {
60     case HA_EXTRA_RESET_STATE: /* Reset state (don't free buffers) */
61       info->lastinx = 0;       /* Use first index as def */
62       info->last_search_keypage = info->lastpos = HA_OFFSET_ERROR;
63       info->page_changed = true;
64       /* Next/prev gives first/last */
65       if (info->opt_flag & READ_CACHE_USED) {
66         reinit_io_cache(&info->rec_cache, READ_CACHE, 0,
67                         (bool)(info->lock_type != F_UNLCK),
68                         (bool)(info->update & HA_STATE_ROW_CHANGED));
69       }
70       info->update = ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
71                       HA_STATE_PREV_FOUND);
72       break;
73     case HA_EXTRA_PREPARE_FOR_UPDATE:
74       if (info->s->data_file_type != DYNAMIC_RECORD) break;
75       /* Remove read/write cache if dynamic rows */
76       // Fall through.
77     case HA_EXTRA_NO_READCHECK:
78       info->opt_flag &= ~READ_CHECK_USED; /* No readcheck */
79       break;
80     case HA_EXTRA_READCHECK:
81       info->opt_flag |= READ_CHECK_USED;
82       break;
83     case HA_EXTRA_KEYREAD: /* Read only keys to record */
84     case HA_EXTRA_REMEMBER_POS:
85       info->opt_flag |= REMEMBER_OLD_POS;
86       memmove((uchar *)info->lastkey + share->base.max_key_length * 2,
87               (uchar *)info->lastkey, info->lastkey_length);
88       info->save_update = info->update;
89       info->save_lastinx = info->lastinx;
90       info->save_lastpos = info->lastpos;
91       info->save_lastkey_length = info->lastkey_length;
92       if (function == HA_EXTRA_REMEMBER_POS) break;
93       /* fall through */
94     case HA_EXTRA_KEYREAD_CHANGE_POS:
95       info->opt_flag |= KEY_READ_USED;
96       info->read_record = _mi_read_key_record;
97       break;
98     case HA_EXTRA_NO_KEYREAD:
99     case HA_EXTRA_RESTORE_POS:
100       if (info->opt_flag & REMEMBER_OLD_POS) {
101         memmove((uchar *)info->lastkey,
102                 (uchar *)info->lastkey + share->base.max_key_length * 2,
103                 info->save_lastkey_length);
104         info->update = info->save_update | HA_STATE_WRITTEN;
105         info->lastinx = info->save_lastinx;
106         info->lastpos = info->save_lastpos;
107         info->lastkey_length = info->save_lastkey_length;
108       }
109       info->read_record = share->read_record;
110       info->opt_flag &= ~(KEY_READ_USED | REMEMBER_OLD_POS);
111       break;
112     case HA_EXTRA_NO_USER_CHANGE: /* Database is somehow locked agains changes
113                                    */
114       info->lock_type = F_EXTRA_LCK; /* Simulate as locked */
115       break;
116     case HA_EXTRA_WAIT_LOCK:
117       info->lock_wait = 0;
118       break;
119     case HA_EXTRA_NO_WAIT_LOCK:
120       info->lock_wait = MY_DONT_WAIT;
121       break;
122     case HA_EXTRA_NO_KEYS:
123       if (info->lock_type == F_UNLCK) {
124         error = 1; /* Not possibly if not lock */
125         break;
126       }
127       if (mi_is_any_key_active(share->state.key_map)) {
128         MI_KEYDEF *key = share->keyinfo;
129         uint i;
130         for (i = 0; i < share->base.keys; i++, key++) {
131           if (!(key->flag & HA_NOSAME) && info->s->base.auto_key != i + 1) {
132             mi_clear_key_active(share->state.key_map, i);
133             info->update |= HA_STATE_CHANGED;
134           }
135         }
136 
137         if (!share->changed) {
138           share->state.changed |= STATE_CHANGED | STATE_NOT_ANALYZED;
139           share->changed = true; /* Update on close */
140           if (!share->global_changed) {
141             share->global_changed = true;
142             share->state.open_count++;
143           }
144         }
145         share->state.state = *info->state;
146         error = mi_state_info_write(share->kfile, &share->state, 1 | 2);
147       }
148       break;
149     case HA_EXTRA_FORCE_REOPEN:
150       mysql_mutex_lock(&THR_LOCK_myisam);
151       share->last_version = 0L; /* Impossible version */
152       mysql_mutex_unlock(&THR_LOCK_myisam);
153       break;
154     case HA_EXTRA_PREPARE_FOR_DROP:
155       mysql_mutex_lock(&THR_LOCK_myisam);
156       share->last_version = 0L; /* Impossible version */
157       mysql_mutex_unlock(&THR_LOCK_myisam);
158       break;
159     case HA_EXTRA_FLUSH:
160       if (!share->temporary)
161         flush_key_blocks(share->key_cache, keycache_thread_var(), share->kfile,
162                          FLUSH_KEEP);
163 #ifndef _WIN32
164       _mi_decrement_open_count(info);
165 #endif
166       if (share->not_flushed) {
167         share->not_flushed = false;
168         if (mysql_file_sync(share->kfile, MYF(0))) error = my_errno();
169         if (mysql_file_sync(info->dfile, MYF(0))) error = my_errno();
170         if (error) {
171           share->changed = true;
172           mi_print_error(info->s, HA_ERR_CRASHED);
173           mi_mark_crashed(info); /* Fatal error found */
174         }
175       }
176       if (share->base.blobs) mi_alloc_rec_buff(info, -1, &info->rec_buff);
177       break;
178     case HA_EXTRA_NORMAL: /* Theese isn't in use */
179       info->quick_mode = false;
180       break;
181     case HA_EXTRA_QUICK:
182       info->quick_mode = true;
183       break;
184     case HA_EXTRA_NO_ROWS:
185       if (!share->state.header.uniques) info->opt_flag |= OPT_NO_ROWS;
186       break;
187     case HA_EXTRA_PRELOAD_BUFFER_SIZE:
188       info->preload_buff_size = *((ulong *)extra_arg);
189       break;
190     case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
191     case HA_EXTRA_CHANGE_KEY_TO_DUP:
192       mi_extra_keyflag(info, function);
193       break;
194     case HA_EXTRA_MARK_AS_LOG_TABLE:
195       mysql_mutex_lock(&share->intern_lock);
196       share->is_log_table = true;
197       mysql_mutex_unlock(&share->intern_lock);
198       break;
199     default:
200       break;
201   }
202   {
203     char tmp[1];
204     tmp[0] = function;
205     myisam_log_command(MI_LOG_EXTRA, info, (uchar *)tmp, 1, error);
206   }
207   return error;
208 } /* mi_extra */
209 
mi_set_index_cond_func(MI_INFO * info,index_cond_func_t func,void * func_arg)210 void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func,
211                             void *func_arg) {
212   info->index_cond_func = func;
213   info->index_cond_func_arg = func_arg;
214 }
215 
216 /*
217     Start/Stop Inserting Duplicates Into a Table, WL#1648.
218  */
mi_extra_keyflag(MI_INFO * info,enum ha_extra_function function)219 static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function) {
220   uint idx;
221 
222   for (idx = 0; idx < info->s->base.keys; idx++) {
223     switch (function) {
224       case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
225         info->s->keyinfo[idx].flag |= HA_NOSAME;
226         break;
227       case HA_EXTRA_CHANGE_KEY_TO_DUP:
228         info->s->keyinfo[idx].flag &= ~(HA_NOSAME);
229         break;
230       default:
231         break;
232     }
233   }
234 }
235 
mi_reset(MI_INFO * info)236 int mi_reset(MI_INFO *info) {
237   int error = 0;
238   MYISAM_SHARE *share = info->s;
239   DBUG_TRACE;
240   /*
241     Free buffers and reset the following flags:
242     EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK
243 
244     If the row buffer cache is large (for dynamic tables), reduce it
245     to save memory.
246   */
247   if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) {
248     info->opt_flag &= ~(READ_CACHE_USED | WRITE_CACHE_USED);
249     error = end_io_cache(&info->rec_cache);
250   }
251   if (share->base.blobs) mi_alloc_rec_buff(info, -1, &info->rec_buff);
252 #if defined(HAVE_MADVISE)
253   if (info->opt_flag & MEMMAP_USED)
254     madvise((char *)share->file_map, share->state.state.data_file_length,
255             MADV_RANDOM);
256 #endif
257   info->opt_flag &= ~(KEY_READ_USED | REMEMBER_OLD_POS);
258   info->quick_mode = false;
259   info->lastinx = 0; /* Use first index as def */
260   info->last_search_keypage = info->lastpos = HA_OFFSET_ERROR;
261   info->page_changed = true;
262   info->update = ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
263                   HA_STATE_PREV_FOUND);
264   return error;
265 }
266