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
26int 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