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 02110-1335 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 
maria_rkey(MARIA_HA * info,uchar * buf,int inx,const uchar * key_data,key_part_map keypart_map,enum ha_rkey_function search_flag)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 
ma_yield_and_check_if_killed(MARIA_HA * info,int inx)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