MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mi_range.c
1 /*
2  Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3 
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; version 2 of the License.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
17 
18 /*
19  Gives a approximated number of how many records there is between two keys.
20  Used when optimizing querries.
21  */
22 
23 #include "myisamdef.h"
24 #include "rt_index.h"
25 
26 static ha_rows _mi_record_pos(MI_INFO *, const uchar *, key_part_map,
27  enum ha_rkey_function);
28 static double _mi_search_pos(MI_INFO *,MI_KEYDEF *,uchar *, uint,uint,my_off_t);
29 static uint _mi_keynr(MI_INFO *info,MI_KEYDEF *,uchar *, uchar *,uint *);
30 
31 /*
32  Estimate how many records there is in a given range
33 
34  SYNOPSIS
35  mi_records_in_range()
36  info MyISAM handler
37  inx Index to use
38  min_key Min key. Is = 0 if no min range
39  max_key Max key. Is = 0 if no max range
40 
41  NOTES
42  We should ONLY return 0 if there is no rows in range
43 
44  RETURN
45  HA_POS_ERROR error (or we can't estimate number of rows)
46  number Estimated number of rows
47 */
48 
49 ha_rows mi_records_in_range(MI_INFO *info, int inx,
50  key_range *min_key, key_range *max_key)
51 {
52  ha_rows start_pos,end_pos,res;
53  DBUG_ENTER("mi_records_in_range");
54 
55  if ((inx = _mi_check_index(info,inx)) < 0)
56  DBUG_RETURN(HA_POS_ERROR);
57 
58  if (fast_mi_readinfo(info))
59  DBUG_RETURN(HA_POS_ERROR);
60  info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED);
61  if (info->s->concurrent_insert)
62  mysql_rwlock_rdlock(&info->s->key_root_lock[inx]);
63 
64  switch(info->s->keyinfo[inx].key_alg){
65 #ifdef HAVE_RTREE_KEYS
66  case HA_KEY_ALG_RTREE:
67  {
68  uchar * key_buff;
69  uint start_key_len;
70 
71  /*
72  The problem is that the optimizer doesn't support
73  RTree keys properly at the moment.
74  Hope this will be fixed some day.
75  But now NULL in the min_key means that we
76  didn't make the task for the RTree key
77  and expect BTree functionality from it.
78  As it's not able to handle such request
79  we return the error.
80  */
81  if (!min_key)
82  {
83  res= HA_POS_ERROR;
84  break;
85  }
86  key_buff= info->lastkey+info->s->base.max_key_length;
87  start_key_len= _mi_pack_key(info,inx, key_buff,
88  (uchar*) min_key->key, min_key->keypart_map,
89  (HA_KEYSEG**) 0);
90  res= rtree_estimate(info, inx, key_buff, start_key_len,
91  myisam_read_vec[min_key->flag]);
92  res= res ? res : 1; /* Don't return 0 */
93  break;
94  }
95 #endif
96  case HA_KEY_ALG_BTREE:
97  default:
98  start_pos= (min_key ? _mi_record_pos(info, min_key->key,
99  min_key->keypart_map, min_key->flag)
100  : (ha_rows) 0);
101  end_pos= (max_key ? _mi_record_pos(info, max_key->key,
102  max_key->keypart_map, max_key->flag)
103  : info->state->records + (ha_rows) 1);
104  res= (end_pos < start_pos ? (ha_rows) 0 :
105  (end_pos == start_pos ? (ha_rows) 1 : end_pos-start_pos));
106  if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR)
107  res=HA_POS_ERROR;
108  }
109 
110  if (info->s->concurrent_insert)
111  mysql_rwlock_unlock(&info->s->key_root_lock[inx]);
112  fast_mi_writeinfo(info);
113 
114  DBUG_PRINT("info",("records: %ld",(ulong) (res)));
115  DBUG_RETURN(res);
116 }
117 
118 
119  /* Find relative position (in records) for key in index-tree */
120 
121 static ha_rows _mi_record_pos(MI_INFO *info, const uchar *key,
122  key_part_map keypart_map,
123  enum ha_rkey_function search_flag)
124 {
125  uint inx=(uint) info->lastinx, nextflag, key_len;
126  MI_KEYDEF *keyinfo=info->s->keyinfo+inx;
127  uchar *key_buff;
128  double pos;
129 
130  DBUG_ENTER("_mi_record_pos");
131  DBUG_PRINT("enter",("search_flag: %d",search_flag));
132  DBUG_ASSERT(keypart_map);
133 
134  key_buff=info->lastkey+info->s->base.max_key_length;
135  key_len=_mi_pack_key(info,inx,key_buff,(uchar*) key, keypart_map,
136  (HA_KEYSEG**) 0);
137  DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,
138  (uchar*) key_buff,key_len););
139  nextflag=myisam_read_vec[search_flag];
140  if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
141  key_len=USE_WHOLE_KEY;
142 
143  /*
144  my_compare.c:ha_compare_text() has a flag 'skip_end_space'.
145  This is set in my_compare.c:ha_key_cmp() in dependence on the
146  compare flags 'nextflag' and the column type.
147 
148  TEXT columns are of type HA_KEYTYPE_VARTEXT. In this case the
149  condition is skip_end_space= ((nextflag & (SEARCH_FIND |
150  SEARCH_UPDATE)) == SEARCH_FIND).
151 
152  SEARCH_FIND is used for an exact key search. The combination
153  SEARCH_FIND | SEARCH_UPDATE is used in write/update/delete
154  operations with a comment like "Not real duplicates", whatever this
155  means. From the condition above we can see that 'skip_end_space' is
156  always false for these operations. The result is that trailing space
157  counts in key comparison and hence, emtpy strings ('', string length
158  zero, but not NULL) compare less that strings starting with control
159  characters and these in turn compare less than strings starting with
160  blanks.
161 
162  When estimating the number of records in a key range, we request an
163  exact search for the minimum key. This translates into a plain
164  SEARCH_FIND flag. Using this alone would lead to a 'skip_end_space'
165  compare. Empty strings would be expected above control characters.
166  Their keys would not be found because they are located below control
167  characters.
168 
169  This is the reason that we add the SEARCH_UPDATE flag here. It makes
170  the key estimation compare in the same way like key write operations
171  do. Olny so we will find the keys where they have been inserted.
172 
173  Adding the flag unconditionally does not hurt as it is used in the
174  above mentioned condition only. So it can safely be used together
175  with other flags.
176  */
177  pos=_mi_search_pos(info,keyinfo,key_buff,key_len,
178  nextflag | SEARCH_SAVE_BUFF | SEARCH_UPDATE,
179  info->s->state.key_root[inx]);
180  if (pos >= 0.0)
181  {
182  DBUG_PRINT("exit",("pos: %ld",(ulong) (pos*info->state->records)));
183  DBUG_RETURN((ulong) (pos*info->state->records+0.5));
184  }
185  DBUG_RETURN(HA_POS_ERROR);
186 }
187 
188 
189  /* This is a modified version of _mi_search */
190  /* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
191 
192 static double _mi_search_pos(register MI_INFO *info,
193  register MI_KEYDEF *keyinfo,
194  uchar *key, uint key_len, uint nextflag,
195  register my_off_t pos)
196 {
197  int flag;
198  uint nod_flag,keynr,UNINIT_VAR(max_keynr);
199  my_bool after_key;
200  uchar *keypos,*buff;
201  double offset;
202  DBUG_ENTER("_mi_search_pos");
203 
204  if (pos == HA_OFFSET_ERROR)
205  DBUG_RETURN(0.5);
206 
207  if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1)))
208  goto err;
209  flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
210  &keypos,info->lastkey, &after_key);
211  nod_flag=mi_test_if_nod(buff);
212  keynr=_mi_keynr(info,keyinfo,buff,keypos,&max_keynr);
213 
214  if (flag)
215  {
216  if (flag == MI_FOUND_WRONG_KEY)
217  DBUG_RETURN(-1); /* error */
218  /*
219  Didn't found match. keypos points at next (bigger) key
220  Try to find a smaller, better matching key.
221  Matches keynr + [0-1]
222  */
223  if (flag > 0 && ! nod_flag)
224  offset= 1.0;
225  else if ((offset=_mi_search_pos(info,keyinfo,key,key_len,nextflag,
226  _mi_kpos(nod_flag,keypos))) < 0)
227  DBUG_RETURN(offset);
228  }
229  else
230  {
231  /*
232  Found match. Keypos points at the start of the found key
233  Matches keynr+1
234  */
235  offset=1.0; /* Matches keynr+1 */
236  if ((nextflag & SEARCH_FIND) && nod_flag &&
237  ((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME ||
238  key_len != USE_WHOLE_KEY))
239  {
240  /*
241  There may be identical keys in the tree. Try to match on of those.
242  Matches keynr + [0-1]
243  */
244  if ((offset=_mi_search_pos(info,keyinfo,key,key_len,SEARCH_FIND,
245  _mi_kpos(nod_flag,keypos))) < 0)
246  DBUG_RETURN(offset); /* Read error */
247  }
248  }
249  DBUG_PRINT("info",("keynr: %d offset: %g max_keynr: %d nod: %d flag: %d",
250  keynr,offset,max_keynr,nod_flag,flag));
251  DBUG_RETURN((keynr+offset)/(max_keynr+1));
252 err:
253  DBUG_PRINT("exit",("Error: %d",my_errno));
254  DBUG_RETURN (-1.0);
255 }
256 
257 
258  /* Get keynummer of current key and max number of keys in nod */
259 
260 static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
261  uchar *keypos, uint *ret_max_key)
262 {
263  uint nod_flag,keynr,max_key;
264  uchar t_buff[MI_MAX_KEY_BUFF],*end;
265 
266  end= page+mi_getint(page);
267  nod_flag=mi_test_if_nod(page);
268  page+=2+nod_flag;
269 
270  if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
271  {
272  *ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag);
273  return (uint) (keypos-page)/(keyinfo->keylength+nod_flag);
274  }
275 
276  max_key=keynr=0;
277  t_buff[0]=0; /* Safety */
278  while (page < end)
279  {
280  if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff))
281  return 0; /* Error */
282  max_key++;
283  if (page == keypos)
284  keynr=max_key;
285  }
286  *ret_max_key=max_key;
287  return(keynr);
288 }