1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* Read record based on a key */
24 
25 #include "myisamdef.h"
26 #include "rt_index.h"
27 
28 	/* Read a record using key */
29 	/* Ordinary search_flag is 0 ; Give error if no record with key */
30 
mi_rkey(MI_INFO * info,uchar * buf,int inx,const uchar * key,key_part_map keypart_map,enum ha_rkey_function search_flag)31 int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
32             key_part_map keypart_map, enum ha_rkey_function search_flag)
33 {
34   uchar *key_buff;
35   MYISAM_SHARE *share=info->s;
36   MI_KEYDEF *keyinfo;
37   HA_KEYSEG *last_used_keyseg;
38   uint pack_key_length, use_key_length, nextflag;
39   uint myisam_search_flag;
40   int res= 0;
41   DBUG_ENTER("mi_rkey");
42   DBUG_PRINT("enter", ("base: 0x%lx  buf: 0x%lx  inx: %d  search_flag: %d",
43                        (long) info, (long) buf, inx, search_flag));
44 
45   if ((inx = _mi_check_index(info,inx)) < 0)
46     DBUG_RETURN(my_errno);
47 
48   info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
49   info->last_key_func= search_flag;
50   keyinfo= share->keyinfo + inx;
51 
52   if (info->once_flags & USE_PACKED_KEYS)
53   {
54     info->once_flags&= ~USE_PACKED_KEYS;	/* Reset flag */
55     /*
56       key is already packed!;  This happens when we are using a MERGE TABLE
57       In this key 'key_part_map' is the length of the key !
58     */
59     key_buff=info->lastkey+info->s->base.max_key_length;
60     pack_key_length= keypart_map;
61     bmove(key_buff, key, pack_key_length);
62     last_used_keyseg= info->s->keyinfo[inx].seg + info->last_used_keyseg;
63   }
64   else
65   {
66     DBUG_ASSERT(keypart_map);
67     /* Save the packed key for later use in the second buffer of lastkey. */
68     key_buff=info->lastkey+info->s->base.max_key_length;
69     pack_key_length=_mi_pack_key(info,(uint) inx, key_buff, (uchar*) key,
70 				 keypart_map, &last_used_keyseg);
71     /* Save packed_key_length for use by the MERGE engine. */
72     info->pack_key_length= pack_key_length;
73     info->last_used_keyseg= (uint16) (last_used_keyseg -
74                                       info->s->keyinfo[inx].seg);
75     DBUG_EXECUTE("info",_mi_print_key(DBUG_FILE, keyinfo->seg,
76 				     key_buff, pack_key_length););
77   }
78 
79   if (fast_mi_readinfo(info))
80     goto err;
81 
82   if (share->concurrent_insert)
83     mysql_rwlock_rdlock(&share->key_root_lock[inx]);
84 
85   nextflag=myisam_read_vec[search_flag];
86   use_key_length=pack_key_length;
87   if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
88     use_key_length=USE_WHOLE_KEY;
89 
90   switch (info->s->keyinfo[inx].key_alg) {
91 #ifdef HAVE_RTREE_KEYS
92   case HA_KEY_ALG_RTREE:
93     if (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0)
94     {
95       mi_print_error(info->s, HA_ERR_CRASHED);
96       my_errno=HA_ERR_CRASHED;
97       if (share->concurrent_insert)
98         mysql_rwlock_unlock(&share->key_root_lock[inx]);
99       goto err;
100     }
101     break;
102 #endif
103   case HA_KEY_ALG_BTREE:
104   default:
105     myisam_search_flag= myisam_read_vec[search_flag];
106     if (!_mi_search(info, keyinfo, key_buff, use_key_length,
107                     myisam_search_flag, info->s->state.key_root[inx]))
108     {
109       /*
110         Found a key, but it might not be usable. We cannot use rows that
111         are inserted by other threads after we got our table lock
112         ("concurrent inserts"). The record may not even be present yet.
113         Keys are inserted into the index(es) before the record is
114         inserted into the data file. When we got our table lock, we
115         saved the current data_file_length. Concurrent inserts always go
116         to the end of the file. So we can test if the found key
117         references a new record.
118 
119         If we are searching for a partial key (or using >, >=, < or <=) and
120         the data is outside of the data file, we need to continue searching
121         for the first key inside the data file.
122 
123         We do also continue searching if an index condition check function
124         is available.
125       */
126       while ((info->lastpos >= info->state->data_file_length &&
127               (search_flag != HA_READ_KEY_EXACT ||
128               last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) ||
129              (info->index_cond_func &&
130               !(res= mi_check_index_cond(info, inx, buf))))
131       {
132         uint not_used[2];
133         /*
134           Skip rows that are inserted by other threads since we got a lock
135           Note that this can only happen if we are not searching after an
136           full length exact key, because the keys are sorted
137           according to position
138         */
139         if  (_mi_search_next(info, keyinfo, info->lastkey,
140                              info->lastkey_length,
141                              myisam_readnext_vec[search_flag],
142                              info->s->state.key_root[inx]))
143           break;
144         /*
145           Check that the found key does still match the search.
146           _mi_search_next() delivers the next key regardless of its
147           value.
148         */
149         if (search_flag == HA_READ_KEY_EXACT &&
150             ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
151                        SEARCH_FIND, not_used))
152         {
153           my_errno= HA_ERR_KEY_NOT_FOUND;
154           info->lastpos= HA_OFFSET_ERROR;
155           break;
156         }
157       }
158       if (res == 2)
159       {
160         info->lastpos= HA_OFFSET_ERROR;
161         if (share->concurrent_insert)
162           mysql_rwlock_unlock(&share->key_root_lock[inx]);
163         DBUG_RETURN((my_errno= HA_ERR_KEY_NOT_FOUND));
164       }
165       /*
166         Error if no row found within the data file. (Bug #29838)
167         Do not overwrite my_errno if already at HA_OFFSET_ERROR.
168       */
169       if (info->lastpos != HA_OFFSET_ERROR &&
170           info->lastpos >= info->state->data_file_length)
171       {
172         info->lastpos= HA_OFFSET_ERROR;
173         my_errno= HA_ERR_KEY_NOT_FOUND;
174       }
175     }
176   }
177   if (share->concurrent_insert)
178     mysql_rwlock_unlock(&share->key_root_lock[inx]);
179 
180   /* Calculate length of the found key;  Used by mi_rnext_same */
181   if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
182       info->lastpos != HA_OFFSET_ERROR)
183     info->last_rkey_length= _mi_keylength_part(keyinfo, info->lastkey,
184 					       last_used_keyseg);
185   else
186     info->last_rkey_length= pack_key_length;
187 
188   /* Next call to mi_rnext_same should set rnext_same_key. */
189   info->set_rnext_same_key= TRUE;
190 
191   /* Check if we don't want to have record back, only error message */
192   if (!buf)
193     DBUG_RETURN(info->lastpos == HA_OFFSET_ERROR ? my_errno : 0);
194 
195   if (!(*info->read_record)(info,info->lastpos,buf))
196   {
197     info->update|= HA_STATE_AKTIV;		/* Record is read */
198     DBUG_RETURN(0);
199   }
200 
201   info->lastpos = HA_OFFSET_ERROR;		/* Didn't find key */
202 
203   /* Store last used key as a base for read next */
204   memcpy(info->lastkey,key_buff,pack_key_length);
205   info->last_rkey_length= pack_key_length;
206   memset(info->lastkey+pack_key_length, 0, info->s->base.rec_reflength);
207   info->lastkey_length=pack_key_length+info->s->base.rec_reflength;
208 
209   if (search_flag == HA_READ_AFTER_KEY)
210     info->update|=HA_STATE_NEXT_FOUND;		/* Previous gives last row */
211 err:
212   DBUG_RETURN(my_errno);
213 } /* _mi_rkey */
214