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