MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mi_rkey.c
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 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 /* Read record based on a key */
17 
18 #include "myisamdef.h"
19 #include "rt_index.h"
20 
21  /* Read a record using key */
22  /* Ordinary search_flag is 0 ; Give error if no record with key */
23 
24 int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
25  key_part_map keypart_map, enum ha_rkey_function search_flag)
26 {
27  uchar *key_buff;
28  MYISAM_SHARE *share=info->s;
29  MI_KEYDEF *keyinfo;
30  HA_KEYSEG *last_used_keyseg;
31  uint pack_key_length, use_key_length, nextflag;
32  uint myisam_search_flag;
33  int res= 0;
34  DBUG_ENTER("mi_rkey");
35  DBUG_PRINT("enter", ("base: 0x%lx buf: 0x%lx inx: %d search_flag: %d",
36  (long) info, (long) buf, inx, search_flag));
37 
38  if ((inx = _mi_check_index(info,inx)) < 0)
39  DBUG_RETURN(my_errno);
40 
41  info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
42  info->last_key_func= search_flag;
43  keyinfo= share->keyinfo + inx;
44 
45  if (info->once_flags & USE_PACKED_KEYS)
46  {
47  info->once_flags&= ~USE_PACKED_KEYS; /* Reset flag */
48  /*
49  key is already packed!; This happens when we are using a MERGE TABLE
50  In this key 'key_part_map' is the length of the key !
51  */
52  key_buff=info->lastkey+info->s->base.max_key_length;
53  pack_key_length= keypart_map;
54  bmove(key_buff, key, pack_key_length);
55  last_used_keyseg= info->s->keyinfo[inx].seg + info->last_used_keyseg;
56  }
57  else
58  {
59  DBUG_ASSERT(keypart_map);
60  /* Save the packed key for later use in the second buffer of lastkey. */
61  key_buff=info->lastkey+info->s->base.max_key_length;
62  pack_key_length=_mi_pack_key(info,(uint) inx, key_buff, (uchar*) key,
63  keypart_map, &last_used_keyseg);
64  /* Save packed_key_length for use by the MERGE engine. */
65  info->pack_key_length= pack_key_length;
66  info->last_used_keyseg= (uint16) (last_used_keyseg -
67  info->s->keyinfo[inx].seg);
68  DBUG_EXECUTE("info",_mi_print_key(DBUG_FILE, keyinfo->seg,
69  key_buff, pack_key_length););
70  }
71 
72  if (fast_mi_readinfo(info))
73  goto err;
74 
75  if (share->concurrent_insert)
76  mysql_rwlock_rdlock(&share->key_root_lock[inx]);
77 
78  nextflag=myisam_read_vec[search_flag];
79  use_key_length=pack_key_length;
80  if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
81  use_key_length=USE_WHOLE_KEY;
82 
83  switch (info->s->keyinfo[inx].key_alg) {
84 #ifdef HAVE_RTREE_KEYS
85  case HA_KEY_ALG_RTREE:
86  if (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0)
87  {
88  mi_print_error(info->s, HA_ERR_CRASHED);
89  my_errno=HA_ERR_CRASHED;
90  if (share->concurrent_insert)
91  mysql_rwlock_unlock(&share->key_root_lock[inx]);
92  goto err;
93  }
94  break;
95 #endif
96  case HA_KEY_ALG_BTREE:
97  default:
98  myisam_search_flag= myisam_read_vec[search_flag];
99  if (!_mi_search(info, keyinfo, key_buff, use_key_length,
100  myisam_search_flag, info->s->state.key_root[inx]))
101  {
102  /*
103  Found a key, but it might not be usable. We cannot use rows that
104  are inserted by other threads after we got our table lock
105  ("concurrent inserts"). The record may not even be present yet.
106  Keys are inserted into the index(es) before the record is
107  inserted into the data file. When we got our table lock, we
108  saved the current data_file_length. Concurrent inserts always go
109  to the end of the file. So we can test if the found key
110  references a new record.
111 
112  If we are searching for a partial key (or using >, >=, < or <=) and
113  the data is outside of the data file, we need to continue searching
114  for the first key inside the data file.
115 
116  We do also continue searching if an index condition check function
117  is available.
118  */
119  while ((info->lastpos >= info->state->data_file_length &&
120  (search_flag != HA_READ_KEY_EXACT ||
121  last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) ||
122  (info->index_cond_func &&
123  !(res= mi_check_index_cond(info, inx, buf))))
124  {
125  uint not_used[2];
126  /*
127  Skip rows that are inserted by other threads since we got a lock
128  Note that this can only happen if we are not searching after an
129  full length exact key, because the keys are sorted
130  according to position
131  */
132  if (_mi_search_next(info, keyinfo, info->lastkey,
133  info->lastkey_length,
134  myisam_readnext_vec[search_flag],
135  info->s->state.key_root[inx]))
136  break;
137  /*
138  Check that the found key does still match the search.
139  _mi_search_next() delivers the next key regardless of its
140  value.
141  */
142  if (search_flag == HA_READ_KEY_EXACT &&
143  ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
144  SEARCH_FIND, not_used))
145  {
146  my_errno= HA_ERR_KEY_NOT_FOUND;
147  info->lastpos= HA_OFFSET_ERROR;
148  break;
149  }
150  }
151  if (res == 2)
152  {
153  info->lastpos= HA_OFFSET_ERROR;
154  if (share->concurrent_insert)
155  mysql_rwlock_unlock(&share->key_root_lock[inx]);
156  DBUG_RETURN((my_errno= HA_ERR_KEY_NOT_FOUND));
157  }
158  /*
159  Error if no row found within the data file. (Bug #29838)
160  Do not overwrite my_errno if already at HA_OFFSET_ERROR.
161  */
162  if (info->lastpos != HA_OFFSET_ERROR &&
163  info->lastpos >= info->state->data_file_length)
164  {
165  info->lastpos= HA_OFFSET_ERROR;
166  my_errno= HA_ERR_KEY_NOT_FOUND;
167  }
168  }
169  }
170  if (share->concurrent_insert)
171  mysql_rwlock_unlock(&share->key_root_lock[inx]);
172 
173  /* Calculate length of the found key; Used by mi_rnext_same */
174  if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
175  info->lastpos != HA_OFFSET_ERROR)
176  info->last_rkey_length= _mi_keylength_part(keyinfo, info->lastkey,
177  last_used_keyseg);
178  else
179  info->last_rkey_length= pack_key_length;
180 
181  /* Check if we don't want to have record back, only error message */
182  if (!buf)
183  DBUG_RETURN(info->lastpos == HA_OFFSET_ERROR ? my_errno : 0);
184 
185  if (!(*info->read_record)(info,info->lastpos,buf))
186  {
187  info->update|= HA_STATE_AKTIV; /* Record is read */
188  DBUG_RETURN(0);
189  }
190 
191  info->lastpos = HA_OFFSET_ERROR; /* Didn't find key */
192 
193  /* Store last used key as a base for read next */
194  memcpy(info->lastkey,key_buff,pack_key_length);
195  info->last_rkey_length= pack_key_length;
196  memset(info->lastkey+pack_key_length, 0, info->s->base.rec_reflength);
197  info->lastkey_length=pack_key_length+info->s->base.rec_reflength;
198 
199  if (search_flag == HA_READ_AFTER_KEY)
200  info->update|=HA_STATE_NEXT_FOUND; /* Previous gives last row */
201 err:
202  DBUG_RETURN(my_errno);
203 } /* _mi_rkey */