1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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     memmove(key_buff, key, pack_key_length);
62     last_used_keyseg= info->s->keyinfo[inx].seg + info->last_used_keyseg;
63   }
64   else
65   {
66     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   case HA_KEY_ALG_RTREE:
92     if (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0)
93     {
94       // rtree_find_first will return -1 for an empty index,
95       // but it's not a crash.
96       if (my_errno() != HA_ERR_END_OF_FILE || info->lastpos != HA_OFFSET_ERROR ||
97           info->s->state.state.records != 0)
98       {
99         mi_print_error(info->s, HA_ERR_CRASHED);
100         set_my_errno(HA_ERR_CRASHED);
101       }
102       if (share->concurrent_insert)
103         mysql_rwlock_unlock(&share->key_root_lock[inx]);
104       goto err;
105     }
106     break;
107   case HA_KEY_ALG_BTREE:
108   default:
109     myisam_search_flag= myisam_read_vec[search_flag];
110     if (!_mi_search(info, keyinfo, key_buff, use_key_length,
111                     myisam_search_flag, info->s->state.key_root[inx]))
112     {
113       /*
114         Found a key, but it might not be usable. We cannot use rows that
115         are inserted by other threads after we got our table lock
116         ("concurrent inserts"). The record may not even be present yet.
117         Keys are inserted into the index(es) before the record is
118         inserted into the data file. When we got our table lock, we
119         saved the current data_file_length. Concurrent inserts always go
120         to the end of the file. So we can test if the found key
121         references a new record.
122 
123         If we are searching for a partial key (or using >, >=, < or <=) and
124         the data is outside of the data file, we need to continue searching
125         for the first key inside the data file.
126 
127         We do also continue searching if an index condition check function
128         is available.
129       */
130       while ((info->lastpos >= info->state->data_file_length &&
131               (search_flag != HA_READ_KEY_EXACT ||
132               last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) ||
133              (info->index_cond_func &&
134               !(res= mi_check_index_cond(info, inx, buf))))
135       {
136         uint not_used[2];
137         /*
138           Skip rows that are inserted by other threads since we got a lock
139           Note that this can only happen if we are not searching after an
140           full length exact key, because the keys are sorted
141           according to position
142         */
143         if  (_mi_search_next(info, keyinfo, info->lastkey,
144                              info->lastkey_length,
145                              myisam_readnext_vec[search_flag],
146                              info->s->state.key_root[inx]))
147           break;
148         /*
149           Check that the found key does still match the search.
150           _mi_search_next() delivers the next key regardless of its
151           value.
152         */
153         if (search_flag == HA_READ_KEY_EXACT &&
154             ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
155                        SEARCH_FIND, not_used))
156         {
157           set_my_errno(HA_ERR_KEY_NOT_FOUND);
158           info->lastpos= HA_OFFSET_ERROR;
159           break;
160         }
161       }
162       if (res == 2)
163       {
164         info->lastpos= HA_OFFSET_ERROR;
165         if (share->concurrent_insert)
166           mysql_rwlock_unlock(&share->key_root_lock[inx]);
167         set_my_errno(HA_ERR_KEY_NOT_FOUND);
168         DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
169       }
170       /*
171         Error if no row found within the data file. (Bug #29838)
172         Do not overwrite my_errno if already at HA_OFFSET_ERROR.
173       */
174       if (info->lastpos != HA_OFFSET_ERROR &&
175           info->lastpos >= info->state->data_file_length)
176       {
177         info->lastpos= HA_OFFSET_ERROR;
178         set_my_errno(HA_ERR_KEY_NOT_FOUND);
179       }
180     }
181   }
182   if (share->concurrent_insert)
183     mysql_rwlock_unlock(&share->key_root_lock[inx]);
184 
185   /* Calculate length of the found key;  Used by mi_rnext_same */
186   if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
187       info->lastpos != HA_OFFSET_ERROR)
188     info->last_rkey_length= _mi_keylength_part(keyinfo, info->lastkey,
189 					       last_used_keyseg);
190   else
191     info->last_rkey_length= pack_key_length;
192 
193   /* Next call to mi_rnext_same should set rnext_same_key. */
194   info->set_rnext_same_key= TRUE;
195 
196   /* Check if we don't want to have record back, only error message */
197   if (!buf)
198     DBUG_RETURN(info->lastpos == HA_OFFSET_ERROR ? my_errno() : 0);
199 
200   if (!(*info->read_record)(info,info->lastpos,buf))
201   {
202     info->update|= HA_STATE_AKTIV;		/* Record is read */
203     DBUG_RETURN(0);
204   }
205 
206   info->lastpos = HA_OFFSET_ERROR;		/* Didn't find key */
207 
208   /* Store last used key as a base for read next */
209   memcpy(info->lastkey,key_buff,pack_key_length);
210   info->last_rkey_length= pack_key_length;
211   memset(info->lastkey+pack_key_length, 0, info->s->base.rec_reflength);
212   info->lastkey_length=pack_key_length+info->s->base.rec_reflength;
213 
214   if (search_flag == HA_READ_AFTER_KEY)
215     info->update|=HA_STATE_NEXT_FOUND;		/* Previous gives last row */
216 err:
217   DBUG_RETURN(my_errno());
218 } /* _mi_rkey */
219