1 /*
2 Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24 /* Update an old row in a MyISAM table */
25
26 #include "fulltext.h"
27 #include "rt_index.h"
28
mi_update(MI_INFO * info,const uchar * oldrec,uchar * newrec)29 int mi_update(MI_INFO *info, const uchar *oldrec, uchar *newrec)
30 {
31 int flag,key_changed,save_errno;
32 my_off_t pos;
33 uint i;
34 uchar old_key[MI_MAX_KEY_BUFF],*new_key;
35 my_bool auto_key_changed=0;
36 ulonglong changed;
37 MYISAM_SHARE *share=info->s;
38 ha_checksum old_checksum= 0;
39 DBUG_ENTER("mi_update");
40
41 DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage",
42 mi_print_error(info->s, HA_ERR_CRASHED);
43 set_my_errno(HA_ERR_CRASHED);
44 DBUG_RETURN(HA_ERR_CRASHED););
45 if (!(info->update & HA_STATE_AKTIV))
46 {
47 set_my_errno(HA_ERR_KEY_NOT_FOUND);
48 DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
49 }
50 if (share->options & HA_OPTION_READ_ONLY_DATA)
51 {
52 set_my_errno(EACCES);
53 DBUG_RETURN(EACCES);
54 }
55 if (info->state->key_file_length >= share->base.margin_key_file_length)
56 {
57 set_my_errno(HA_ERR_INDEX_FILE_FULL);
58 DBUG_RETURN(HA_ERR_INDEX_FILE_FULL);
59 }
60 pos=info->lastpos;
61 if (_mi_readinfo(info,F_WRLCK,1))
62 DBUG_RETURN(my_errno());
63
64 if (share->calc_checksum)
65 old_checksum=info->checksum=(*share->calc_checksum)(info,oldrec);
66 if ((*share->compare_record)(info,oldrec))
67 {
68 save_errno=my_errno();
69 goto err_end; /* Record has changed */
70 }
71
72
73 /* Calculate and check all unique constraints */
74 key_changed=0;
75 for (i=0 ; i < share->state.header.uniques ; i++)
76 {
77 MI_UNIQUEDEF *def=share->uniqueinfo+i;
78 if (mi_unique_comp(def, newrec, oldrec,1) &&
79 mi_check_unique(info, def, newrec, mi_unique_hash(def, newrec),
80 info->lastpos))
81 {
82 save_errno=my_errno();
83 goto err_end;
84 }
85 }
86 if (_mi_mark_file_changed(info))
87 {
88 save_errno=my_errno();
89 goto err_end;
90 }
91
92 /* Check which keys changed from the original row */
93
94 new_key=info->lastkey2;
95 changed=0;
96 for (i=0 ; i < share->base.keys ; i++)
97 {
98 if (mi_is_key_active(share->state.key_map, i))
99 {
100 if (share->keyinfo[i].flag & HA_FULLTEXT )
101 {
102 if (_mi_ft_cmp(info,i,oldrec, newrec))
103 {
104 if ((int) i == info->lastinx)
105 {
106 /*
107 We are changeing the index we are reading on. Mark that
108 the index data has changed and we need to do a full search
109 when doing read-next
110 */
111 key_changed|=HA_STATE_WRITTEN;
112 }
113 changed|=((ulonglong) 1 << i);
114 if (_mi_ft_update(info,i, old_key,oldrec,newrec,pos))
115 goto err;
116 }
117 }
118 else
119 {
120 uint new_length=_mi_make_key(info,i,new_key,newrec,pos);
121 uint old_length=_mi_make_key(info,i,old_key,oldrec,pos);
122
123 if (new_length != old_length ||
124 memcmp((uchar*) old_key,(uchar*) new_key,new_length))
125 {
126 if ((int) i == info->lastinx)
127 key_changed|=HA_STATE_WRITTEN; /* Mark that keyfile changed */
128 changed|=((ulonglong) 1 << i);
129 share->keyinfo[i].version++;
130 if (share->keyinfo[i].ck_delete(info,i,old_key,old_length)) goto err;
131 if (share->keyinfo[i].ck_insert(info,i,new_key,new_length)) goto err;
132 if (share->base.auto_key == i+1)
133 auto_key_changed=1;
134 }
135 }
136 }
137 }
138 /*
139 If we are running with external locking, we must update the index file
140 that something has changed.
141 */
142 if (changed || !my_disable_locking)
143 key_changed|= HA_STATE_CHANGED;
144
145 if (share->calc_checksum)
146 {
147 info->checksum=(*share->calc_checksum)(info,newrec);
148 /* Store new checksum in index file header */
149 key_changed|= HA_STATE_CHANGED;
150 }
151 {
152 /*
153 Don't update index file if data file is not extended and no status
154 information changed
155 */
156 MI_STATUS_INFO state;
157 ha_rows org_split;
158 my_off_t org_delete_link;
159
160 memcpy((char*) &state, (char*) info->state, sizeof(state));
161 org_split= share->state.split;
162 org_delete_link= share->state.dellink;
163 if ((*share->update_record)(info,pos,newrec))
164 goto err;
165 if (!key_changed &&
166 (memcmp((char*) &state, (char*) info->state, sizeof(state)) ||
167 org_split != share->state.split ||
168 org_delete_link != share->state.dellink))
169 key_changed|= HA_STATE_CHANGED; /* Must update index file */
170 }
171 if (auto_key_changed)
172 set_if_bigger(info->s->state.auto_increment,
173 retrieve_auto_increment(info, newrec));
174 if (share->calc_checksum)
175 info->state->checksum+=(info->checksum - old_checksum);
176
177 info->update= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED | HA_STATE_AKTIV |
178 key_changed);
179 myisam_log_record(MI_LOG_UPDATE,info,newrec,info->lastpos,0);
180 /*
181 Every myisam function that updates myisam table must end with
182 call to _mi_writeinfo(). If operation (second param of
183 _mi_writeinfo()) is not 0 it sets share->changed to 1, that is
184 flags that data has changed. If operation is 0, this function
185 equals to no-op in this case.
186
187 mi_update() must always pass !0 value as operation, since even if
188 there is no index change there could be data change.
189 */
190 (void) _mi_writeinfo(info, WRITEINFO_UPDATE_KEYFILE);
191 if (info->invalidator != 0)
192 {
193 DBUG_PRINT("info", ("invalidator... '%s' (update)", info->filename));
194 (*info->invalidator)(info->filename);
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 if (changed)
203 key_changed|= HA_STATE_CHANGED;
204 if (my_errno() == HA_ERR_FOUND_DUPP_KEY || my_errno() == HA_ERR_RECORD_FILE_FULL ||
205 my_errno() == HA_ERR_NULL_IN_SPATIAL || my_errno() == HA_ERR_OUT_OF_MEM)
206 {
207 info->errkey= (int) i;
208 flag=0;
209 do
210 {
211 if (((ulonglong) 1 << i) & changed)
212 {
213 if (share->keyinfo[i].flag & HA_FULLTEXT)
214 {
215 if ((flag++ && _mi_ft_del(info,i, new_key,newrec,pos)) ||
216 _mi_ft_add(info,i, old_key,oldrec,pos))
217 break;
218 }
219 else
220 {
221 uint new_length=_mi_make_key(info,i,new_key,newrec,pos);
222 uint old_length= _mi_make_key(info,i,old_key,oldrec,pos);
223 if ((flag++ &&
224 share->keyinfo[i].ck_delete(info, i, new_key, new_length)) ||
225 share->keyinfo[i].ck_insert(info, i, old_key, old_length))
226 break;
227 }
228 }
229 } while (i-- != 0);
230 }
231 else
232 {
233 mi_print_error(info->s, HA_ERR_CRASHED);
234 mi_mark_crashed(info);
235 }
236 info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_ROW_CHANGED |
237 key_changed);
238
239 err_end:
240 myisam_log_record(MI_LOG_UPDATE,info,newrec,info->lastpos,my_errno());
241 (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
242 if (save_errno == HA_ERR_KEY_NOT_FOUND)
243 {
244 mi_print_error(info->s, HA_ERR_CRASHED);
245 save_errno=HA_ERR_CRASHED;
246 }
247 set_my_errno(save_errno);
248 DBUG_RETURN(save_errno);
249 } /* mi_update */
250