| 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 | /* Remove all rows from a MARIA table */ |
| 17 | /* This clears the status information and truncates files */ |
| 18 | |
| 19 | #include "maria_def.h" |
| 20 | #include "trnman.h" |
| 21 | |
| 22 | /** |
| 23 | @brief deletes all rows from a table |
| 24 | |
| 25 | @param info Maria handler |
| 26 | |
| 27 | @note It is important that this function does not rely on the state |
| 28 | information, as it may be called by ma_apply_undo_bulk_insert() on an |
| 29 | inconsistent table left by a crash. |
| 30 | |
| 31 | @return Operation status |
| 32 | @retval 0 ok |
| 33 | @retval 1 error |
| 34 | */ |
| 35 | |
| 36 | int maria_delete_all_rows(MARIA_HA *info) |
| 37 | { |
| 38 | MARIA_SHARE *share= info->s; |
| 39 | my_bool log_record; |
| 40 | LSN lsn; |
| 41 | DBUG_ENTER("maria_delete_all_rows" ); |
| 42 | |
| 43 | if (share->options & HA_OPTION_READ_ONLY_DATA) |
| 44 | { |
| 45 | DBUG_RETURN(my_errno=EACCES); |
| 46 | } |
| 47 | /** |
| 48 | @todo LOCK take X-lock on table here. |
| 49 | When we have versioning, if some other thread is looking at this table, |
| 50 | we cannot shrink the file like this. |
| 51 | */ |
| 52 | if (_ma_readinfo(info,F_WRLCK,1)) |
| 53 | DBUG_RETURN(my_errno); |
| 54 | log_record= share->now_transactional && !share->temporary; |
| 55 | |
| 56 | if (log_record) |
| 57 | { |
| 58 | /* |
| 59 | This record will be used by Recovery to finish the deletion if it |
| 60 | crashed. We force it to have a complete history in the log. |
| 61 | */ |
| 62 | LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 1]; |
| 63 | uchar log_data[FILEID_STORE_SIZE]; |
| 64 | log_array[TRANSLOG_INTERNAL_PARTS + 0].str= log_data; |
| 65 | log_array[TRANSLOG_INTERNAL_PARTS + 0].length= sizeof(log_data); |
| 66 | if (unlikely(translog_write_record(&lsn, LOGREC_REDO_DELETE_ALL, |
| 67 | info->trn, info, 0, |
| 68 | sizeof(log_array)/sizeof(log_array[0]), |
| 69 | log_array, log_data, NULL) || |
| 70 | translog_flush(lsn))) |
| 71 | goto err; |
| 72 | /* |
| 73 | If we fail in this function after this point, log and table will be |
| 74 | inconsistent. |
| 75 | */ |
| 76 | if (_ma_mark_file_changed(share)) |
| 77 | goto err; |
| 78 | } |
| 79 | else |
| 80 | { |
| 81 | if (_ma_mark_file_changed(share)) |
| 82 | goto err; |
| 83 | /* Other branch called function below when writing log record, in hook */ |
| 84 | _ma_reset_status(info); |
| 85 | } |
| 86 | /* Remove old history as the table is now empty for everyone */ |
| 87 | _ma_reset_state(info); |
| 88 | share->state.changed= 0; |
| 89 | |
| 90 | /* |
| 91 | If we are using delayed keys or if the user has done changes to the tables |
| 92 | since it was locked then there may be key blocks in the page cache. Or |
| 93 | there may be data blocks there. We need to throw them away or they may |
| 94 | re-enter the emptied table or another table later. |
| 95 | */ |
| 96 | |
| 97 | #ifdef HAVE_MMAP |
| 98 | if (share->file_map) |
| 99 | _ma_unmap_file(info); |
| 100 | #endif |
| 101 | |
| 102 | if (_ma_flush_table_files(info, MARIA_FLUSH_DATA|MARIA_FLUSH_INDEX, |
| 103 | FLUSH_IGNORE_CHANGED, FLUSH_IGNORE_CHANGED) || |
| 104 | mysql_file_chsize(info->dfile.file, 0, 0, MYF(MY_WME)) || |
| 105 | mysql_file_chsize(share->kfile.file, share->base.keystart, 0, MYF(MY_WME))) |
| 106 | goto err; |
| 107 | |
| 108 | if (_ma_initialize_data_file(share, info->dfile.file)) |
| 109 | goto err; |
| 110 | |
| 111 | if (log_record) |
| 112 | { |
| 113 | /* |
| 114 | Because LOGREC_REDO_DELETE_ALL does not operate on pages, it has the |
| 115 | following problem: |
| 116 | delete_all; inserts (redo_insert); all pages get flushed; checkpoint: |
| 117 | the dirty pages list will be empty. In recovery, delete_all is executed, |
| 118 | but redo_insert are skipped (dirty pages list is empty). |
| 119 | To avoid this, we need to set skip_redo_lsn now, and thus need to sync |
| 120 | files. |
| 121 | Also fixes the problem of: |
| 122 | bulk insert; insert; delete_all; crash: |
| 123 | "bulk insert" is skipped (no REDOs), so if "insert" would not be skipped |
| 124 | (if we didn't update skip_redo_lsn below) then "insert" would be tried |
| 125 | and fail, saying that it sees that the first page has to be created |
| 126 | though the inserted row has rownr>0. |
| 127 | */ |
| 128 | my_bool error= _ma_state_info_write(share, |
| 129 | MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET | |
| 130 | MA_STATE_INFO_WRITE_LOCK) || |
| 131 | _ma_update_state_lsns(share, lsn, trnman_get_min_trid(), FALSE, FALSE) || |
| 132 | _ma_sync_table_files(info); |
| 133 | info->trn->rec_lsn= LSN_IMPOSSIBLE; |
| 134 | if (error) |
| 135 | goto err; |
| 136 | } |
| 137 | |
| 138 | if (info->opt_flag & WRITE_CACHE_USED) |
| 139 | reinit_io_cache(&info->rec_cache, WRITE_CACHE, 0, 1, 1); |
| 140 | |
| 141 | _ma_writeinfo(info, WRITEINFO_UPDATE_KEYFILE); |
| 142 | #ifdef HAVE_MMAP |
| 143 | /* Map again */ |
| 144 | if (share->file_map) |
| 145 | _ma_dynmap_file(info, (my_off_t) 0); |
| 146 | #endif |
| 147 | DBUG_RETURN(0); |
| 148 | |
| 149 | err: |
| 150 | { |
| 151 | int save_errno=my_errno; |
| 152 | _ma_writeinfo(info, WRITEINFO_UPDATE_KEYFILE); |
| 153 | info->update|=HA_STATE_WRITTEN; /* Buffer changed */ |
| 154 | DBUG_RETURN(my_errno=save_errno); |
| 155 | } |
| 156 | } /* maria_delete_all_rows */ |
| 157 | |
| 158 | |
| 159 | /* |
| 160 | Reset status information |
| 161 | |
| 162 | SYNOPSIS |
| 163 | _ma_reset_status() |
| 164 | maria Maria handler |
| 165 | |
| 166 | DESCRIPTION |
| 167 | Resets data and index file information as if the file would be empty |
| 168 | Files are not touched. |
| 169 | */ |
| 170 | |
| 171 | void _ma_reset_status(MARIA_HA *info) |
| 172 | { |
| 173 | MARIA_SHARE *share= info->s; |
| 174 | MARIA_STATE_INFO *state= &share->state; |
| 175 | uint i; |
| 176 | DBUG_ENTER("_ma_reset_status" ); |
| 177 | |
| 178 | state->split= 0; |
| 179 | state->state.records= state->state.del= 0; |
| 180 | state->changed= 0; /* File is optimized */ |
| 181 | state->dellink= HA_OFFSET_ERROR; |
| 182 | state->sortkey= (ushort) ~0; |
| 183 | state->state.key_file_length= share->base.keystart; |
| 184 | state->state.data_file_length= 0; |
| 185 | state->state.empty= state->state.key_empty= 0; |
| 186 | state->state.checksum= 0; |
| 187 | share->state.open_count= 0; |
| 188 | share->global_changed= 0; |
| 189 | |
| 190 | share->changed= 1; /* We must write state */ |
| 191 | |
| 192 | *info->state= state->state; |
| 193 | |
| 194 | /* Drop the delete key chain. */ |
| 195 | state->key_del= HA_OFFSET_ERROR; |
| 196 | /* Clear all keys */ |
| 197 | for (i=0 ; i < share->base.keys ; i++) |
| 198 | state->key_root[i]= HA_OFFSET_ERROR; |
| 199 | DBUG_VOID_RETURN; |
| 200 | } |
| 201 | |