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 02110-1335 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 "ma_ftdefs.h"
24 #include "ma_crypt.h"
25
maria_close(register MARIA_HA * info)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 /* Check that file is not part of any uncommitted transactions */
41 DBUG_ASSERT(info->trn == 0 || info->trn == &dummy_transaction_object);
42
43 if (share->reopen == 1)
44 {
45 /*
46 If we are going to close the file, flush page cache without
47 a global mutex
48 */
49 if (flush_pagecache_blocks(share->pagecache, &share->kfile,
50 share->deleting ? FLUSH_IGNORE_CHANGED : 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 maria_ftparser_call_deinitializer(info);
90 my_free(info->rec_buff);
91 (*share->end)(info);
92
93 if (flag)
94 {
95 /* Last close of file; Flush everything */
96
97 /* Check that we don't have any dangling pointers from the transaction */
98 DBUG_ASSERT(share->in_trans == 0);
99 DBUG_ASSERT(share->open_list == 0);
100
101 if (share->kfile.file >= 0)
102 {
103 my_bool save_global_changed= share->global_changed;
104
105 /* Avoid _ma_mark_file_changed() when flushing pages */
106 share->global_changed= 1;
107
108 if ((*share->once_end)(share))
109 error= my_errno;
110 /*
111 Extra flush, just in case someone opened and closed the file
112 since the start of the function (very unlikely)
113 */
114 if (flush_pagecache_blocks(share->pagecache, &share->kfile,
115 share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
116 error= my_errno;
117 unmap_file(info);
118 if (!internal_table &&
119 (((share->changed && share->base.born_transactional) ||
120 maria_is_crashed(info) ||
121 (share->temporary && !share->deleting))))
122 {
123 if (save_global_changed)
124 {
125 /*
126 Reset effect of _ma_mark_file_changed(). Better to do it
127 here than in _ma_decrement_open_count(), as
128 _ma_state_info_write() will write the open_count.
129 */
130 save_global_changed= 0;
131 share->state.open_count--;
132 }
133 /*
134 State must be written to file as it was not done at table's
135 unlocking.
136 */
137 if (_ma_state_info_write(share, MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET))
138 error= my_errno;
139 }
140 DBUG_ASSERT(maria_is_crashed(info) || !share->base.born_transactional ||
141 share->state.open_count == 0 ||
142 share->open_count_not_zero_on_open);
143
144 /* Ensure that open_count is zero on close */
145 share->global_changed= save_global_changed;
146 _ma_decrement_open_count(info, 0);
147
148 /* Ensure that open_count really is zero */
149 DBUG_ASSERT(maria_is_crashed(info) || share->temporary ||
150 share->state.open_count == 0 ||
151 share->open_count_not_zero_on_open);
152
153 /*
154 File must be synced as it is going out of the maria_open_list and so
155 becoming unknown to future Checkpoints.
156 */
157 if (share->now_transactional && mysql_file_sync(share->kfile.file, MYF(MY_WME)))
158 error= my_errno;
159 if (mysql_file_close(share->kfile.file, MYF(0)))
160 error= my_errno;
161 }
162 thr_lock_delete(&share->lock);
163 mysql_mutex_destroy(&share->key_del_lock);
164
165 {
166 int i,keys;
167 keys = share->state.header.keys;
168 mysql_rwlock_destroy(&share->mmap_lock);
169 for(i=0; i<keys; i++) {
170 mysql_rwlock_destroy(&share->keyinfo[i].root_lock);
171 }
172 }
173 DBUG_ASSERT(share->now_transactional == share->base.born_transactional);
174 /*
175 We assign -1 because checkpoint does not need to flush (in case we
176 have concurrent checkpoint if no then we do not need it here also)
177 */
178 share->kfile.file= -1;
179
180 /*
181 Remember share->history for future opens
182
183 We have to unlock share->intern_lock then lock it after
184 LOCK_trn_list (trnman_lock()) to avoid dead locks.
185 */
186 if (!internal_table)
187 mysql_mutex_unlock(&share->intern_lock);
188 _ma_remove_not_visible_states_with_lock(share, TRUE);
189 if (!internal_table)
190 mysql_mutex_lock(&share->intern_lock);
191
192 if (share->in_checkpoint & MARIA_CHECKPOINT_LOOKS_AT_ME)
193 {
194 /* we cannot my_free() the share, Checkpoint would see a bad pointer */
195 share->in_checkpoint|= MARIA_CHECKPOINT_SHOULD_FREE_ME;
196 }
197 else
198 share_can_be_freed= TRUE;
199
200 if (share->state_history)
201 {
202 if (share->state_history->trid) /* If not visible for all */
203 {
204 MARIA_STATE_HISTORY_CLOSED *history;
205 DBUG_PRINT("info", ("Storing state history"));
206 /*
207 Here we ignore the unlikely case that we don't have memory
208 to store the state. In the worst case what happens is that
209 any transaction that tries to access this table will get a
210 wrong status information.
211 */
212 if ((history= (MARIA_STATE_HISTORY_CLOSED *)
213 my_malloc(sizeof(*history), MYF(MY_WME))))
214 {
215 history->create_rename_lsn= share->state.create_rename_lsn;
216 history->state_history= share->state_history;
217 if (my_hash_insert(&maria_stored_state, (uchar*) history))
218 my_free(history);
219 }
220 }
221 else
222 my_free(share->state_history);
223 /* Marker for concurrent checkpoint */
224 share->state_history= 0;
225 }
226 }
227 if (!internal_table)
228 {
229 mysql_mutex_unlock(&THR_LOCK_maria);
230 mysql_mutex_unlock(&share->intern_lock);
231 mysql_mutex_unlock(&share->close_lock);
232 }
233 if (share_can_be_freed)
234 {
235 ma_crypt_free(share);
236 (void) mysql_mutex_destroy(&share->intern_lock);
237 (void) mysql_mutex_destroy(&share->close_lock);
238 (void) mysql_cond_destroy(&share->key_del_cond);
239 my_free(share);
240 /*
241 If share cannot be freed, it's because checkpoint has previously
242 recorded to include this share in the checkpoint and so is soon going to
243 look at some of its content (share->in_checkpoint/id/last_version).
244 */
245 }
246 my_free(info->ftparser_param);
247 if (info->dfile.file >= 0)
248 {
249 /*
250 This is outside of mutex so would confuse a concurrent
251 Checkpoint. Fortunately in BLOCK_RECORD we close earlier under mutex.
252 */
253 if (mysql_file_close(info->dfile.file, MYF(0)))
254 error= my_errno;
255 }
256
257 delete_dynamic(&info->pinned_pages);
258 my_free(info);
259
260 if (error)
261 {
262 DBUG_PRINT("error", ("Got error on close: %d", my_errno));
263 DBUG_RETURN(my_errno= error);
264 }
265 DBUG_RETURN(0);
266 } /* maria_close */
267