1 | /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB |
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 as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
15 | |
16 | /* Read record based on a key */ |
17 | |
18 | #include "maria_def.h" |
19 | #include "ma_rt_index.h" |
20 | |
21 | /** |
22 | Read a record using key |
23 | |
24 | @note |
25 | Ordinary search_flag is 0 ; Give error if no record with key |
26 | */ |
27 | |
28 | int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data, |
29 | key_part_map keypart_map, enum ha_rkey_function search_flag) |
30 | { |
31 | uchar *key_buff; |
32 | MARIA_SHARE *share= info->s; |
33 | MARIA_KEYDEF *keyinfo; |
34 | HA_KEYSEG *last_used_keyseg; |
35 | uint32 nextflag; |
36 | MARIA_KEY key; |
37 | ICP_RESULT icp_res= ICP_MATCH; |
38 | DBUG_ENTER("maria_rkey" ); |
39 | DBUG_PRINT("enter" , ("base:%p buf:%p inx: %d search_flag: %d" , |
40 | info, buf, inx, search_flag)); |
41 | |
42 | if ((inx = _ma_check_index(info,inx)) < 0) |
43 | DBUG_RETURN(my_errno); |
44 | |
45 | info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); |
46 | info->last_key_func= search_flag; |
47 | keyinfo= info->last_key.keyinfo; |
48 | |
49 | key_buff= info->lastkey_buff2; |
50 | |
51 | if (info->once_flags & USE_PACKED_KEYS) |
52 | { |
53 | info->once_flags&= ~USE_PACKED_KEYS; /* Reset flag */ |
54 | /* |
55 | key is already packed!; This happens when we are using a MERGE TABLE |
56 | In this key 'key_part_map' is the length of the key ! |
57 | */ |
58 | if (key_buff != key_data) |
59 | bmove(key_buff, key_data, keypart_map); |
60 | key.data= key_buff; |
61 | key.keyinfo= keyinfo; |
62 | key.data_length= keypart_map; |
63 | key.ref_length= 0; |
64 | key.flag= 0; |
65 | |
66 | last_used_keyseg= keyinfo->seg + info->last_used_keyseg; |
67 | } |
68 | else |
69 | { |
70 | DBUG_ASSERT(keypart_map); |
71 | /* Save the packed key for later use in the second buffer of lastkey. */ |
72 | _ma_pack_key(info, &key, inx, key_buff, key_data, |
73 | keypart_map, &last_used_keyseg); |
74 | /* Save packed_key_length for use by the MERGE engine. */ |
75 | info->pack_key_length= key.data_length; |
76 | info->last_used_keyseg= (uint16) (last_used_keyseg - |
77 | keyinfo->seg); |
78 | DBUG_EXECUTE("key" , _ma_print_key(DBUG_FILE, &key);); |
79 | } |
80 | |
81 | if (fast_ma_readinfo(info)) |
82 | goto err; |
83 | if (share->lock_key_trees) |
84 | mysql_rwlock_rdlock(&keyinfo->root_lock); |
85 | |
86 | nextflag= maria_read_vec[search_flag] | key.flag; |
87 | if (search_flag != HA_READ_KEY_EXACT) |
88 | { |
89 | /* Assume we will get a read next/previous call after this one */ |
90 | nextflag|= SEARCH_SAVE_BUFF; |
91 | } |
92 | switch (keyinfo->key_alg) { |
93 | #ifdef HAVE_RTREE_KEYS |
94 | case HA_KEY_ALG_RTREE: |
95 | if (maria_rtree_find_first(info, &key, nextflag) < 0) |
96 | { |
97 | _ma_set_fatal_error(share, HA_ERR_CRASHED); |
98 | info->cur_row.lastpos= HA_OFFSET_ERROR; |
99 | } |
100 | break; |
101 | #endif |
102 | case HA_KEY_ALG_BTREE: |
103 | default: |
104 | if (!_ma_search(info, &key, nextflag, info->s->state.key_root[inx])) |
105 | { |
106 | MARIA_KEY lastkey; |
107 | /* |
108 | Found a key, but it might not be usable. We cannot use rows that |
109 | are inserted by other threads after we got our table lock |
110 | ("concurrent inserts"). The record may not even be present yet. |
111 | Keys are inserted into the index(es) before the record is |
112 | inserted into the data file. |
113 | |
114 | If index condition is present, it must be either satisfied or |
115 | not satisfied with an out-of-range condition. |
116 | */ |
117 | if ((*share->row_is_visible)(info) && |
118 | ((icp_res= ma_check_index_cond(info, inx, buf)) != ICP_NO_MATCH)) |
119 | break; |
120 | |
121 | /* The key references a concurrently inserted record. */ |
122 | if (search_flag == HA_READ_KEY_EXACT && |
123 | last_used_keyseg == keyinfo->seg + keyinfo->keysegs) |
124 | { |
125 | /* Simply ignore the key if it matches exactly. (Bug #29838) */ |
126 | my_errno= HA_ERR_KEY_NOT_FOUND; |
127 | info->cur_row.lastpos= HA_OFFSET_ERROR; |
128 | break; |
129 | } |
130 | |
131 | lastkey.keyinfo= keyinfo; |
132 | lastkey.data= info->lastkey_buff; |
133 | do |
134 | { |
135 | uint not_used[2]; |
136 | /* |
137 | Skip rows that are inserted by other threads since we got |
138 | a lock. Note that this can only happen if we are not |
139 | searching after a full length exact key, because the keys |
140 | are sorted according to position. |
141 | */ |
142 | lastkey.data_length= info->last_key.data_length; |
143 | lastkey.ref_length= info->last_key.ref_length; |
144 | lastkey.flag= info->last_key.flag; |
145 | if (_ma_search_next(info, &lastkey, maria_readnext_vec[search_flag], |
146 | info->s->state.key_root[inx])) |
147 | break; /* purecov: inspected */ |
148 | |
149 | /* |
150 | If we are at the last key on the key page, allow writers to |
151 | access the index. |
152 | */ |
153 | if (info->int_keypos >= info->int_maxpos && |
154 | ma_yield_and_check_if_killed(info, inx)) |
155 | { |
156 | DBUG_ASSERT(info->cur_row.lastpos == HA_OFFSET_ERROR); |
157 | break; |
158 | } |
159 | |
160 | /* |
161 | Check that the found key does still match the search. |
162 | _ma_search_next() delivers the next key regardless of its |
163 | value. |
164 | */ |
165 | if (!(nextflag & (SEARCH_BIGGER | SEARCH_SMALLER)) && |
166 | ha_key_cmp(keyinfo->seg, info->last_key.data, key.data, |
167 | key.data_length, SEARCH_FIND, not_used)) |
168 | { |
169 | /* purecov: begin inspected */ |
170 | my_errno= HA_ERR_KEY_NOT_FOUND; |
171 | info->cur_row.lastpos= HA_OFFSET_ERROR; |
172 | break; |
173 | /* purecov: end */ |
174 | } |
175 | |
176 | } while (!(*share->row_is_visible)(info) || |
177 | ((icp_res= ma_check_index_cond(info, inx, buf)) == 0)); |
178 | } |
179 | else |
180 | { |
181 | DBUG_ASSERT(info->cur_row.lastpos); |
182 | } |
183 | } |
184 | if (share->lock_key_trees) |
185 | mysql_rwlock_unlock(&keyinfo->root_lock); |
186 | |
187 | if (info->cur_row.lastpos == HA_OFFSET_ERROR) |
188 | { |
189 | if (icp_res == ICP_OUT_OF_RANGE) |
190 | { |
191 | /* We don't want HA_ERR_END_OF_FILE in this particular case */ |
192 | my_errno= HA_ERR_KEY_NOT_FOUND; |
193 | } |
194 | fast_ma_writeinfo(info); |
195 | goto err; |
196 | } |
197 | |
198 | /* Calculate length of the found key; Used by maria_rnext_same */ |
199 | if ((keyinfo->flag & HA_VAR_LENGTH_KEY)) |
200 | info->last_rkey_length= _ma_keylength_part(keyinfo, info->lastkey_buff, |
201 | last_used_keyseg); |
202 | else |
203 | info->last_rkey_length= key.data_length; |
204 | |
205 | /* Check if we don't want to have record back, only error message */ |
206 | if (!buf) |
207 | { |
208 | fast_ma_writeinfo(info); |
209 | DBUG_RETURN(0); |
210 | } |
211 | if (!(*info->read_record)(info, buf, info->cur_row.lastpos)) |
212 | { |
213 | info->update|= HA_STATE_AKTIV; /* Record is read */ |
214 | DBUG_RETURN(0); |
215 | } |
216 | |
217 | info->cur_row.lastpos= HA_OFFSET_ERROR; /* Didn't find row */ |
218 | |
219 | err: |
220 | /* Store last used key as a base for read next */ |
221 | memcpy(info->last_key.data, key_buff, key.data_length); |
222 | info->last_key.data_length= key.data_length; |
223 | info->last_key.ref_length= info->s->base.rec_reflength; |
224 | info->last_key.flag= 0; |
225 | /* Create key with rowid 0 */ |
226 | bzero((char*) info->last_key.data + info->last_key.data_length, |
227 | info->s->base.rec_reflength); |
228 | |
229 | if (search_flag == HA_READ_AFTER_KEY) |
230 | info->update|=HA_STATE_NEXT_FOUND; /* Previous gives last row */ |
231 | DBUG_RETURN(my_errno); |
232 | } /* _ma_rkey */ |
233 | |
234 | |
235 | /* |
236 | Yield to possible other writers during a index scan. |
237 | Check also if we got killed by the user and if yes, return |
238 | HA_ERR_LOCK_WAIT_TIMEOUT |
239 | |
240 | return 0 ok |
241 | return 1 Query has been requested to be killed |
242 | */ |
243 | |
244 | my_bool ma_yield_and_check_if_killed(MARIA_HA *info, int inx) |
245 | { |
246 | MARIA_SHARE *share; |
247 | if (ma_killed(info)) |
248 | { |
249 | /* purecov: begin tested */ |
250 | /* Mark that we don't have an active row */ |
251 | info->cur_row.lastpos= HA_OFFSET_ERROR; |
252 | /* Set error that we where aborted by kill from application */ |
253 | my_errno= HA_ERR_ABORTED_BY_USER; |
254 | return 1; |
255 | /* purecov: end */ |
256 | } |
257 | |
258 | if ((share= info->s)->lock_key_trees) |
259 | { |
260 | /* Give writers a chance to access index */ |
261 | mysql_rwlock_unlock(&share->keyinfo[inx].root_lock); |
262 | mysql_rwlock_rdlock(&share->keyinfo[inx].root_lock); |
263 | } |
264 | return 0; |
265 | } |
266 | |
267 | |