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