summaryrefslogtreecommitdiffstats
path: root/storage/maria/ma_close.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/maria/ma_close.c')
-rw-r--r--storage/maria/ma_close.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/storage/maria/ma_close.c b/storage/maria/ma_close.c
new file mode 100644
index 00000000..7441e29a
--- /dev/null
+++ b/storage/maria/ma_close.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ Copyright (c) 2010, 2020, MariaDB Corporation Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/* close a isam-database */
+/*
+ TODO:
+ We need to have a separate mutex on the closed file to allow other threads
+ to open other files during the time we flush the cache and close this file
+*/
+
+#include "ma_ftdefs.h"
+#include "ma_crypt.h"
+#ifdef WITH_S3_STORAGE_ENGINE
+#include "s3_func.h"
+#endif /* WITH_S3_STORAGE_ENGINE */
+
+int maria_close(register MARIA_HA *info)
+{
+ int error=0,flag;
+ MARIA_SHARE *share= info->s;
+ my_bool internal_table= share->internal_table;
+ DBUG_ENTER("maria_close");
+ DBUG_PRINT("enter",("name: '%s' base: %p reopen: %u locks: %u",
+ share->open_file_name.str,
+ info, (uint) share->reopen,
+ (uint) share->tot_locks));
+
+ /* Check that we have unlocked key delete-links properly */
+ DBUG_ASSERT(info->key_del_used == 0);
+ /* Check that file is not part of any uncommitted transactions */
+ DBUG_ASSERT(info->trn == 0 || info->trn == &dummy_transaction_object);
+
+ if (share->reopen == 1)
+ {
+ /*
+ If we are going to close the file, flush page cache without
+ a global mutex
+ */
+ if (flush_pagecache_blocks(share->pagecache, &share->kfile,
+ share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
+ error= my_errno;
+ }
+
+ /* Ensure no one can open this file while we are closing it */
+ if (!internal_table)
+ mysql_mutex_lock(&THR_LOCK_maria);
+ if (info->lock_type == F_EXTRA_LCK)
+ info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */
+
+ if (info->lock_type != F_UNLCK)
+ {
+ if (maria_lock_database(info,F_UNLCK))
+ error=my_errno;
+ }
+ if (!internal_table)
+ {
+ mysql_mutex_lock(&share->close_lock);
+ mysql_mutex_lock(&share->intern_lock);
+ }
+
+ if (share->options & HA_OPTION_READ_ONLY_DATA)
+ {
+ share->r_locks--;
+ share->tot_locks--;
+ }
+ if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
+ {
+ if (end_io_cache(&info->rec_cache))
+ error=my_errno;
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ }
+ flag= !--share->reopen;
+ if (!internal_table)
+ {
+ maria_open_list= list_delete(maria_open_list,&info->open_list);
+ share->open_list= list_delete(share->open_list, &info->share_list);
+ }
+
+ maria_ftparser_call_deinitializer(info);
+ my_free(info->rec_buff);
+ (*share->end)(info);
+
+ if (flag)
+ {
+ /* Last close of file */
+
+ /*
+ Check that we don't have any dangling open files
+ We may still have some open transactions. In this case the share
+ will be kept around until the transaction has closed
+ */
+ DBUG_ASSERT(share->open_list == 0);
+
+ /* Flush everything */
+ if (share->kfile.file >= 0)
+ {
+ my_bool save_global_changed= share->global_changed;
+
+ /* Avoid _ma_mark_file_changed() when flushing pages */
+ share->global_changed= 1;
+
+ /* Flush page cache if BLOCK format */
+ if ((*share->once_end)(share))
+ error= my_errno;
+ /*
+ Extra flush, just in case someone opened and closed the file
+ since the start of the function (very unlikely)
+ */
+ if (flush_pagecache_blocks(share->pagecache, &share->kfile,
+ share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
+ error= my_errno;
+ unmap_file(info);
+ if (!internal_table &&
+ (((share->changed && share->base.born_transactional) ||
+ maria_is_crashed(info) ||
+ (share->temporary && !share->deleting))))
+ {
+ if (save_global_changed)
+ {
+ /*
+ Reset effect of _ma_mark_file_changed(). Better to do it
+ here than in _ma_decrement_open_count(), as
+ _ma_state_info_write() will write the open_count.
+ */
+ save_global_changed= 0;
+ share->state.open_count--;
+ }
+ /*
+ State must be written to file as it was not done at table's
+ unlocking.
+ */
+ if (_ma_state_info_write(share, MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET))
+ error= my_errno;
+ }
+ DBUG_ASSERT(maria_is_crashed(info) || !share->base.born_transactional ||
+ share->state.open_count == 0 ||
+ share->open_count_not_zero_on_open);
+
+ /* Ensure that open_count is zero on close */
+ share->global_changed= save_global_changed;
+ _ma_decrement_open_count(info, 0);
+
+ /* Ensure that open_count really is zero */
+ DBUG_ASSERT(maria_is_crashed(info) || share->temporary ||
+ share->state.open_count == 0 ||
+ share->open_count_not_zero_on_open);
+
+ /*
+ File must be synced as it is going out of the maria_open_list and so
+ becoming unknown to future Checkpoints.
+ */
+ if (share->now_transactional &&
+ mysql_file_sync(share->kfile.file, MYF(MY_WME)))
+ error= my_errno;
+ if (!share->s3_path && mysql_file_close(share->kfile.file, MYF(0)))
+ error= my_errno;
+ }
+ thr_lock_delete(&share->lock);
+ mysql_mutex_destroy(&share->key_del_lock);
+
+ {
+ int i,keys;
+ keys = share->state.header.keys;
+ mysql_rwlock_destroy(&share->mmap_lock);
+ for(i=0; i<keys; i++) {
+ mysql_rwlock_destroy(&share->keyinfo[i].root_lock);
+ }
+ }
+ DBUG_ASSERT(share->now_transactional == share->base.born_transactional ||
+ share->internal_table);
+ /*
+ We assign -1 because checkpoint does not need to flush (in case we
+ have concurrent checkpoint if no then we do not need it here also)
+ */
+ share->kfile.file= -1;
+
+ /*
+ Remember share->history for future opens
+
+ We have to unlock share->intern_lock then lock it after
+ LOCK_trn_list (trnman_lock()) to avoid dead locks.
+ */
+ if (!internal_table)
+ mysql_mutex_unlock(&share->intern_lock);
+ _ma_remove_not_visible_states_with_lock(share, TRUE);
+ if (!internal_table)
+ mysql_mutex_lock(&share->intern_lock);
+
+ if (share->in_checkpoint & MARIA_CHECKPOINT_LOOKS_AT_ME)
+ {
+ /* we cannot my_free() the share, Checkpoint would see a bad pointer */
+ share->in_checkpoint|= MARIA_CHECKPOINT_SHOULD_FREE_ME;
+ }
+
+ if (share->state_history)
+ {
+ if (share->state_history->trid) /* If not visible for all */
+ {
+ MARIA_STATE_HISTORY_CLOSED *history;
+ DBUG_PRINT("info", ("Storing state history"));
+ /*
+ Here we ignore the unlikely case that we don't have memory
+ to store the state. In the worst case what happens is that
+ any transaction that tries to access this table will get a
+ wrong status information.
+ */
+ if ((history= (MARIA_STATE_HISTORY_CLOSED *)
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(*history), MYF(MY_WME))))
+ {
+ history->create_rename_lsn= share->state.create_rename_lsn;
+ history->state_history= share->state_history;
+ if (my_hash_insert(&maria_stored_state, (uchar*) history))
+ my_free(history);
+ }
+ }
+ else
+ my_free(share->state_history);
+ /* Marker for concurrent checkpoint */
+ share->state_history= 0;
+ }
+ }
+ if (!internal_table)
+ {
+ mysql_mutex_unlock(&THR_LOCK_maria);
+ mysql_mutex_unlock(&share->close_lock);
+ }
+
+ /* free_maria_share will free share->internal_lock */
+ free_maria_share(share);
+
+ my_free(info->ftparser_param);
+ if (info->dfile.file >= 0 && ! info->s3)
+ {
+ /*
+ This is outside of mutex so would confuse a concurrent
+ Checkpoint. Fortunately in BLOCK_RECORD we close earlier under mutex.
+ */
+ if (mysql_file_close(info->dfile.file, MYF(0)))
+ error= my_errno;
+ }
+
+ delete_dynamic(&info->pinned_pages);
+#ifdef WITH_S3_STORAGE_ENGINE
+ if (info->s3)
+ s3f.deinit(info->s3);
+#endif /* WITH_S3_STORAGE_ENGINE */
+ my_free(info);
+
+ if (error)
+ {
+ DBUG_PRINT("error", ("Got error on close: %d", my_errno));
+ DBUG_RETURN(my_errno= error);
+ }
+ DBUG_RETURN(0);
+} /* maria_close */
+
+
+/**
+ Free Aria table share
+
+ Note that share will not be freed a long as there are active checkpoints
+ or transactions pointing at the shared object
+*/
+
+void free_maria_share(MARIA_SHARE *share)
+{
+ if (!share->internal_table)
+ mysql_mutex_assert_owner(&share->intern_lock);
+
+ if (!share->reopen && !share->in_trans &&
+ !(share->in_checkpoint & MARIA_CHECKPOINT_SHOULD_FREE_ME))
+ {
+ /* No one can access this share anymore, time to delete it ! */
+ if (!share->internal_table)
+ mysql_mutex_unlock(&share->intern_lock);
+ ma_crypt_free(share);
+ my_free(share->s3_path);
+ (void) mysql_mutex_destroy(&share->intern_lock);
+ (void) mysql_mutex_destroy(&share->close_lock);
+ (void) mysql_cond_destroy(&share->key_del_cond);
+ my_free(share);
+ return;
+ }
+ if (!share->internal_table)
+ mysql_mutex_unlock(&share->intern_lock);
+ return;
+}