1 | /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB |
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 as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
15 | |
16 | /* close a isam-database */ |
17 | /* |
18 | TODO: |
19 | We need to have a separate mutex on the closed file to allow other threads |
20 | to open other files during the time we flush the cache and close this file |
21 | */ |
22 | |
23 | #include "maria_def.h" |
24 | #include "ma_crypt.h" |
25 | |
26 | int maria_close(register MARIA_HA *info) |
27 | { |
28 | int error=0,flag; |
29 | my_bool share_can_be_freed= FALSE; |
30 | MARIA_SHARE *share= info->s; |
31 | my_bool internal_table= share->internal_table; |
32 | DBUG_ENTER("maria_close" ); |
33 | DBUG_PRINT("enter" ,("name: '%s' base: %p reopen: %u locks: %u" , |
34 | share->open_file_name.str, |
35 | info, (uint) share->reopen, |
36 | (uint) share->tot_locks)); |
37 | |
38 | /* Check that we have unlocked key delete-links properly */ |
39 | DBUG_ASSERT(info->key_del_used == 0); |
40 | |
41 | if (share->reopen == 1) |
42 | { |
43 | /* |
44 | If we are going to close the file, flush page cache without |
45 | a global mutex |
46 | */ |
47 | if (flush_pagecache_blocks(share->pagecache, &share->kfile, |
48 | ((share->temporary || share->deleting) ? |
49 | FLUSH_IGNORE_CHANGED : |
50 | FLUSH_RELEASE))) |
51 | error= my_errno; |
52 | } |
53 | |
54 | /* Ensure no one can open this file while we are closing it */ |
55 | if (!internal_table) |
56 | mysql_mutex_lock(&THR_LOCK_maria); |
57 | if (info->lock_type == F_EXTRA_LCK) |
58 | info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */ |
59 | |
60 | if (info->lock_type != F_UNLCK) |
61 | { |
62 | if (maria_lock_database(info,F_UNLCK)) |
63 | error=my_errno; |
64 | } |
65 | if (!internal_table) |
66 | { |
67 | mysql_mutex_lock(&share->close_lock); |
68 | mysql_mutex_lock(&share->intern_lock); |
69 | } |
70 | |
71 | if (share->options & HA_OPTION_READ_ONLY_DATA) |
72 | { |
73 | share->r_locks--; |
74 | share->tot_locks--; |
75 | } |
76 | if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) |
77 | { |
78 | if (end_io_cache(&info->rec_cache)) |
79 | error=my_errno; |
80 | info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); |
81 | } |
82 | flag= !--share->reopen; |
83 | if (!internal_table) |
84 | { |
85 | maria_open_list= list_delete(maria_open_list,&info->open_list); |
86 | share->open_list= list_delete(share->open_list, &info->share_list); |
87 | } |
88 | |
89 | my_free(info->rec_buff); |
90 | (*share->end)(info); |
91 | |
92 | if (flag) |
93 | { |
94 | /* Last close of file; Flush everything */ |
95 | |
96 | /* Check that we don't have any dangling pointers from the transaction */ |
97 | DBUG_ASSERT(share->in_trans == 0); |
98 | DBUG_ASSERT(share->open_list == 0); |
99 | |
100 | if (share->kfile.file >= 0) |
101 | { |
102 | my_bool save_global_changed= share->global_changed; |
103 | |
104 | /* Avoid _ma_mark_file_changed() when flushing pages */ |
105 | share->global_changed= 1; |
106 | |
107 | if ((*share->once_end)(share)) |
108 | error= my_errno; |
109 | /* |
110 | Extra flush, just in case someone opened and closed the file |
111 | since the start of the function (very unlikely) |
112 | */ |
113 | if (flush_pagecache_blocks(share->pagecache, &share->kfile, |
114 | ((share->temporary || share->deleting) ? |
115 | FLUSH_IGNORE_CHANGED : |
116 | FLUSH_RELEASE))) |
117 | error= my_errno; |
118 | #ifdef HAVE_MMAP |
119 | if (share->file_map) |
120 | _ma_unmap_file(info); |
121 | #endif |
122 | /* |
123 | If we are crashed, we can safely flush the current state as it will |
124 | not change the crashed state. |
125 | We can NOT write the state in other cases as other threads |
126 | may be using the file at this point |
127 | IF using --external-locking, which does not apply to Maria. |
128 | */ |
129 | if (((share->changed && share->base.born_transactional) || |
130 | maria_is_crashed(info))) |
131 | { |
132 | if (save_global_changed) |
133 | { |
134 | /* |
135 | Reset effect of _ma_mark_file_changed(). Better to do it |
136 | here than in _ma_decrement_open_count(), as |
137 | _ma_state_info_write() will write the open_count. |
138 | */ |
139 | save_global_changed= 0; |
140 | share->state.open_count--; |
141 | } |
142 | /* |
143 | State must be written to file as it was not done at table's |
144 | unlocking. |
145 | */ |
146 | if (_ma_state_info_write(share, MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET)) |
147 | error= my_errno; |
148 | } |
149 | DBUG_ASSERT(maria_is_crashed(info) || !share->base.born_transactional || |
150 | share->state.open_count == 0 || |
151 | share->open_count_not_zero_on_open); |
152 | |
153 | /* Ensure that open_count is zero on close */ |
154 | share->global_changed= save_global_changed; |
155 | _ma_decrement_open_count(info, 0); |
156 | |
157 | /* Ensure that open_count really is zero */ |
158 | DBUG_ASSERT(maria_is_crashed(info) || share->temporary || |
159 | share->state.open_count == 0 || |
160 | share->open_count_not_zero_on_open); |
161 | |
162 | /* |
163 | File must be synced as it is going out of the maria_open_list and so |
164 | becoming unknown to future Checkpoints. |
165 | */ |
166 | if (share->now_transactional && mysql_file_sync(share->kfile.file, MYF(MY_WME))) |
167 | error= my_errno; |
168 | if (mysql_file_close(share->kfile.file, MYF(0))) |
169 | error= my_errno; |
170 | } |
171 | thr_lock_delete(&share->lock); |
172 | mysql_mutex_destroy(&share->key_del_lock); |
173 | |
174 | { |
175 | int i,keys; |
176 | keys = share->state.header.keys; |
177 | mysql_rwlock_destroy(&share->mmap_lock); |
178 | for(i=0; i<keys; i++) { |
179 | mysql_rwlock_destroy(&share->keyinfo[i].root_lock); |
180 | } |
181 | } |
182 | DBUG_ASSERT(share->now_transactional == share->base.born_transactional); |
183 | /* |
184 | We assign -1 because checkpoint does not need to flush (in case we |
185 | have concurrent checkpoint if no then we do not need it here also) |
186 | */ |
187 | share->kfile.file= -1; |
188 | |
189 | /* |
190 | Remember share->history for future opens |
191 | |
192 | We have to unlock share->intern_lock then lock it after |
193 | LOCK_trn_list (trnman_lock()) to avoid dead locks. |
194 | */ |
195 | if (!internal_table) |
196 | mysql_mutex_unlock(&share->intern_lock); |
197 | _ma_remove_not_visible_states_with_lock(share, TRUE); |
198 | if (!internal_table) |
199 | mysql_mutex_lock(&share->intern_lock); |
200 | |
201 | if (share->in_checkpoint & MARIA_CHECKPOINT_LOOKS_AT_ME) |
202 | { |
203 | /* we cannot my_free() the share, Checkpoint would see a bad pointer */ |
204 | share->in_checkpoint|= MARIA_CHECKPOINT_SHOULD_FREE_ME; |
205 | } |
206 | else |
207 | share_can_be_freed= TRUE; |
208 | |
209 | if (share->state_history) |
210 | { |
211 | if (share->state_history->trid) /* If not visible for all */ |
212 | { |
213 | MARIA_STATE_HISTORY_CLOSED *history; |
214 | DBUG_PRINT("info" , ("Storing state history" )); |
215 | /* |
216 | Here we ignore the unlikely case that we don't have memory |
217 | to store the state. In the worst case what happens is that |
218 | any transaction that tries to access this table will get a |
219 | wrong status information. |
220 | */ |
221 | if ((history= (MARIA_STATE_HISTORY_CLOSED *) |
222 | my_malloc(sizeof(*history), MYF(MY_WME)))) |
223 | { |
224 | history->create_rename_lsn= share->state.create_rename_lsn; |
225 | history->state_history= share->state_history; |
226 | if (my_hash_insert(&maria_stored_state, (uchar*) history)) |
227 | my_free(history); |
228 | } |
229 | } |
230 | else |
231 | my_free(share->state_history); |
232 | /* Marker for concurrent checkpoint */ |
233 | share->state_history= 0; |
234 | } |
235 | } |
236 | if (!internal_table) |
237 | { |
238 | mysql_mutex_unlock(&THR_LOCK_maria); |
239 | mysql_mutex_unlock(&share->intern_lock); |
240 | mysql_mutex_unlock(&share->close_lock); |
241 | } |
242 | if (share_can_be_freed) |
243 | { |
244 | ma_crypt_free(share); |
245 | (void) mysql_mutex_destroy(&share->intern_lock); |
246 | (void) mysql_mutex_destroy(&share->close_lock); |
247 | (void) mysql_cond_destroy(&share->key_del_cond); |
248 | my_free(share); |
249 | /* |
250 | If share cannot be freed, it's because checkpoint has previously |
251 | recorded to include this share in the checkpoint and so is soon going to |
252 | look at some of its content (share->in_checkpoint/id/last_version). |
253 | */ |
254 | } |
255 | my_free(info->ftparser_param); |
256 | if (info->dfile.file >= 0) |
257 | { |
258 | /* |
259 | This is outside of mutex so would confuse a concurrent |
260 | Checkpoint. Fortunately in BLOCK_RECORD we close earlier under mutex. |
261 | */ |
262 | if (mysql_file_close(info->dfile.file, MYF(0))) |
263 | error= my_errno; |
264 | } |
265 | |
266 | delete_dynamic(&info->pinned_pages); |
267 | my_free(info); |
268 | |
269 | if (error) |
270 | { |
271 | DBUG_PRINT("error" , ("Got error on close: %d" , my_errno)); |
272 | DBUG_RETURN(my_errno= error); |
273 | } |
274 | DBUG_RETURN(0); |
275 | } /* maria_close */ |
276 | |