| 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 | #include "ma_fulltext.h" |
| 17 | #include "ma_rt_index.h" |
| 18 | #include "trnman.h" |
| 19 | |
| 20 | /** |
| 21 | Update an old row in a MARIA table |
| 22 | */ |
| 23 | |
| 24 | int maria_update(register MARIA_HA *info, const uchar *oldrec, |
| 25 | const uchar *newrec) |
| 26 | { |
| 27 | int flag,key_changed,save_errno; |
| 28 | reg3 my_off_t pos; |
| 29 | uint i; |
| 30 | uchar old_key_buff[MARIA_MAX_KEY_BUFF], *UNINIT_VAR(new_key_buff); |
| 31 | my_bool auto_key_changed= 0; |
| 32 | ulonglong UNINIT_VAR(changed); |
| 33 | MARIA_SHARE *share= info->s; |
| 34 | MARIA_KEYDEF *keyinfo; |
| 35 | DBUG_ENTER("maria_update" ); |
| 36 | |
| 37 | DBUG_EXECUTE_IF("maria_pretend_crashed_table_on_usage" , |
| 38 | maria_print_error(info->s, HA_ERR_CRASHED); |
| 39 | DBUG_RETURN(my_errno= HA_ERR_CRASHED);); |
| 40 | if (!(info->update & HA_STATE_AKTIV)) |
| 41 | { |
| 42 | DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); |
| 43 | } |
| 44 | if (share->options & HA_OPTION_READ_ONLY_DATA) |
| 45 | { |
| 46 | DBUG_RETURN(my_errno=EACCES); |
| 47 | } |
| 48 | if (share->state.state.key_file_length >= share->base.margin_key_file_length) |
| 49 | { |
| 50 | DBUG_RETURN(my_errno=HA_ERR_INDEX_FILE_FULL); |
| 51 | } |
| 52 | pos= info->cur_row.lastpos; |
| 53 | if (_ma_readinfo(info,F_WRLCK,1)) |
| 54 | DBUG_RETURN(my_errno); |
| 55 | |
| 56 | if ((*share->compare_record)(info,oldrec)) |
| 57 | { |
| 58 | save_errno= my_errno; |
| 59 | DBUG_PRINT("warning" , ("Got error from compare record" )); |
| 60 | goto err_end; /* Record has changed */ |
| 61 | } |
| 62 | |
| 63 | /* Calculate and check all unique constraints */ |
| 64 | key_changed=0; |
| 65 | for (i=0 ; i < share->state.header.uniques ; i++) |
| 66 | { |
| 67 | MARIA_UNIQUEDEF *def=share->uniqueinfo+i; |
| 68 | if (_ma_unique_comp(def, newrec, oldrec,1) && |
| 69 | _ma_check_unique(info, def, newrec, _ma_unique_hash(def, newrec), |
| 70 | pos)) |
| 71 | { |
| 72 | save_errno=my_errno; |
| 73 | goto err_end; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | if (_ma_mark_file_changed(share)) |
| 78 | { |
| 79 | save_errno=my_errno; |
| 80 | goto err_end; |
| 81 | } |
| 82 | |
| 83 | /* Ensure we don't try to restore auto_increment if it doesn't change */ |
| 84 | info->last_auto_increment= ~(ulonglong) 0; |
| 85 | |
| 86 | /* Check which keys changed from the original row */ |
| 87 | |
| 88 | new_key_buff= info->lastkey_buff2; |
| 89 | changed=0; |
| 90 | for (i=0, keyinfo= share->keyinfo ; i < share->base.keys ; i++, keyinfo++) |
| 91 | { |
| 92 | if (maria_is_key_active(share->state.key_map, i)) |
| 93 | { |
| 94 | if (keyinfo->flag & HA_FULLTEXT ) |
| 95 | { |
| 96 | if (_ma_ft_cmp(info,i,oldrec, newrec)) |
| 97 | { |
| 98 | if ((int) i == info->lastinx) |
| 99 | { |
| 100 | /* |
| 101 | We are changeing the index we are reading on. Mark that |
| 102 | the index data has changed and we need to do a full search |
| 103 | when doing read-next |
| 104 | */ |
| 105 | key_changed|=HA_STATE_WRITTEN; |
| 106 | } |
| 107 | changed|=((ulonglong) 1 << i); |
| 108 | if (_ma_ft_update(info,i,old_key_buff,oldrec,newrec,pos)) |
| 109 | goto err; |
| 110 | } |
| 111 | } |
| 112 | else |
| 113 | { |
| 114 | MARIA_KEY new_key, old_key; |
| 115 | |
| 116 | (*keyinfo->make_key)(info,&new_key, i, new_key_buff, newrec, |
| 117 | pos, info->trn->trid); |
| 118 | (*keyinfo->make_key)(info,&old_key, i, old_key_buff, |
| 119 | oldrec, pos, info->cur_row.trid); |
| 120 | |
| 121 | /* The above changed info->lastkey2. Inform maria_rnext_same(). */ |
| 122 | info->update&= ~HA_STATE_RNEXT_SAME; |
| 123 | |
| 124 | if (new_key.data_length != old_key.data_length || |
| 125 | memcmp(old_key.data, new_key.data, new_key.data_length)) |
| 126 | { |
| 127 | if ((int) i == info->lastinx) |
| 128 | key_changed|=HA_STATE_WRITTEN; /* Mark that keyfile changed */ |
| 129 | changed|=((ulonglong) 1 << i); |
| 130 | keyinfo->version++; |
| 131 | if (keyinfo->ck_delete(info,&old_key)) |
| 132 | goto err; |
| 133 | if (keyinfo->ck_insert(info,&new_key)) |
| 134 | goto err; |
| 135 | if (share->base.auto_key == i+1) |
| 136 | auto_key_changed=1; |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | if (share->calc_checksum) |
| 143 | { |
| 144 | /* |
| 145 | We can't use the row based checksum as this doesn't have enough |
| 146 | precision (one byte, while the table's is more bytes). |
| 147 | At least _ma_check_unique() modifies the 'newrec' record, so checksum |
| 148 | has to be computed _after_ it. Nobody apparently modifies 'oldrec'. |
| 149 | We need to pass the old row's checksum down to (*update_record)(), we do |
| 150 | this via info->new_row.checksum (not intuitive but existing code |
| 151 | mandated that cur_row is the new row). |
| 152 | If (*update_record)() fails, table will be marked corrupted so no need |
| 153 | to revert the live checksum change. |
| 154 | */ |
| 155 | info->cur_row.checksum= (*share->calc_checksum)(info, newrec); |
| 156 | info->new_row.checksum= (*share->calc_checksum)(info, oldrec); |
| 157 | info->state->checksum+= info->cur_row.checksum - info->new_row.checksum; |
| 158 | } |
| 159 | |
| 160 | if ((*share->update_record)(info, pos, oldrec, newrec)) |
| 161 | goto err; |
| 162 | |
| 163 | if (auto_key_changed & !share->now_transactional) |
| 164 | { |
| 165 | const HA_KEYSEG *keyseg= share->keyinfo[share->base.auto_key-1].seg; |
| 166 | const uchar *key= newrec + keyseg->start; |
| 167 | set_if_bigger(share->state.auto_increment, |
| 168 | ma_retrieve_auto_increment(key, keyseg->type)); |
| 169 | } |
| 170 | |
| 171 | /* |
| 172 | We can't yet have HA_STATE_AKTIV here, as block_record dosn't support it |
| 173 | */ |
| 174 | info->update= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED | key_changed); |
| 175 | info->row_changes++; |
| 176 | share->state.changed|= STATE_NOT_MOVABLE | STATE_NOT_ZEROFILLED; |
| 177 | info->state->changed= 1; |
| 178 | |
| 179 | /* |
| 180 | Every Maria function that updates Maria table must end with |
| 181 | call to _ma_writeinfo(). If operation (second param of |
| 182 | _ma_writeinfo()) is not 0 it sets share->changed to 1, that is |
| 183 | flags that data has changed. If operation is 0, this function |
| 184 | equals to no-op in this case. |
| 185 | |
| 186 | ma_update() must always pass !0 value as operation, since even if |
| 187 | there is no index change there could be data change. |
| 188 | */ |
| 189 | _ma_writeinfo(info, WRITEINFO_UPDATE_KEYFILE); |
| 190 | if (info->invalidator != 0) |
| 191 | { |
| 192 | DBUG_PRINT("info" , ("invalidator... '%s' (update)" , |
| 193 | share->open_file_name.str)); |
| 194 | (*info->invalidator)(share->open_file_name.str); |
| 195 | info->invalidator=0; |
| 196 | } |
| 197 | DBUG_RETURN(0); |
| 198 | |
| 199 | err: |
| 200 | DBUG_PRINT("error" ,("key: %d errno: %d" ,i,my_errno)); |
| 201 | save_errno= my_errno; |
| 202 | DBUG_ASSERT(save_errno); |
| 203 | if (!save_errno) |
| 204 | save_errno= HA_ERR_INTERNAL_ERROR; /* Should never happen */ |
| 205 | |
| 206 | if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_OUT_OF_MEM || |
| 207 | my_errno == HA_ERR_RECORD_FILE_FULL) |
| 208 | { |
| 209 | info->errkey= (int) i; |
| 210 | flag=0; |
| 211 | do |
| 212 | { |
| 213 | if (((ulonglong) 1 << i) & changed) |
| 214 | { |
| 215 | if (share->keyinfo[i].flag & HA_FULLTEXT) |
| 216 | { |
| 217 | if ((flag++ && _ma_ft_del(info,i,new_key_buff,newrec,pos)) || |
| 218 | _ma_ft_add(info,i,old_key_buff,oldrec,pos)) |
| 219 | { |
| 220 | _ma_set_fatal_error(share, my_errno); |
| 221 | break; |
| 222 | } |
| 223 | } |
| 224 | else |
| 225 | { |
| 226 | MARIA_KEY new_key, old_key; |
| 227 | (*share->keyinfo[i].make_key)(info, &new_key, i, new_key_buff, |
| 228 | newrec, pos, |
| 229 | info->trn->trid); |
| 230 | (*share->keyinfo[i].make_key)(info, &old_key, i, old_key_buff, |
| 231 | oldrec, pos, info->cur_row.trid); |
| 232 | if ((flag++ && _ma_ck_delete(info, &new_key)) || |
| 233 | _ma_ck_write(info, &old_key)) |
| 234 | { |
| 235 | _ma_set_fatal_error(share, my_errno); |
| 236 | break; |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | } while (i-- != 0); |
| 241 | } |
| 242 | else |
| 243 | _ma_set_fatal_error(share, save_errno); |
| 244 | |
| 245 | info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_ROW_CHANGED | |
| 246 | key_changed); |
| 247 | |
| 248 | err_end: |
| 249 | _ma_writeinfo(info, WRITEINFO_UPDATE_KEYFILE); |
| 250 | if (save_errno == HA_ERR_KEY_NOT_FOUND) |
| 251 | _ma_set_fatal_error(share, HA_ERR_CRASHED); |
| 252 | DBUG_RETURN(my_errno=save_errno); |
| 253 | } /* maria_update */ |
| 254 | |