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