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
28int 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
219err:
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
244my_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