1
by brian
clean slate |
1 |
/* Copyright (C) 2000-2004, 2006 MySQL 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
15 |
||
16 |
/*
|
|
17 |
Gives a approximated number of how many records there is between two keys.
|
|
18 |
Used when optimizing querries.
|
|
19 |
*/
|
|
20 |
||
21 |
#include "myisamdef.h" |
|
22 |
||
23 |
static ha_rows _mi_record_pos(MI_INFO *, const uchar *, key_part_map, |
|
24 |
enum ha_rkey_function); |
|
25 |
static double _mi_search_pos(MI_INFO *,MI_KEYDEF *,uchar *, uint,uint,my_off_t); |
|
26 |
static uint _mi_keynr(MI_INFO *info,MI_KEYDEF *,uchar *, uchar *,uint *); |
|
27 |
||
28 |
/*
|
|
29 |
Estimate how many records there is in a given range
|
|
30 |
||
31 |
SYNOPSIS
|
|
32 |
mi_records_in_range()
|
|
33 |
info MyISAM handler
|
|
34 |
inx Index to use
|
|
35 |
min_key Min key. Is = 0 if no min range
|
|
36 |
max_key Max key. Is = 0 if no max range
|
|
37 |
||
38 |
NOTES
|
|
39 |
We should ONLY return 0 if there is no rows in range
|
|
40 |
||
41 |
RETURN
|
|
42 |
HA_POS_ERROR error (or we can't estimate number of rows)
|
|
43 |
number Estimated number of rows
|
|
44 |
*/
|
|
45 |
||
46 |
ha_rows mi_records_in_range(MI_INFO *info, int inx, |
|
47 |
key_range *min_key, key_range *max_key) |
|
48 |
{
|
|
49 |
ha_rows start_pos,end_pos,res; |
|
50 |
||
51 |
if ((inx = _mi_check_index(info,inx)) < 0) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
52 |
return(HA_POS_ERROR); |
1
by brian
clean slate |
53 |
|
54 |
if (fast_mi_readinfo(info)) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
55 |
return(HA_POS_ERROR); |
1
by brian
clean slate |
56 |
info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED); |
57 |
if (info->s->concurrent_insert) |
|
58 |
rw_rdlock(&info->s->key_root_lock[inx]); |
|
59 |
||
60 |
switch(info->s->keyinfo[inx].key_alg){ |
|
61 |
case HA_KEY_ALG_BTREE: |
|
62 |
default: |
|
63 |
start_pos= (min_key ? _mi_record_pos(info, min_key->key, |
|
64 |
min_key->keypart_map, min_key->flag) |
|
65 |
: (ha_rows) 0); |
|
66 |
end_pos= (max_key ? _mi_record_pos(info, max_key->key, |
|
67 |
max_key->keypart_map, max_key->flag) |
|
68 |
: info->state->records + (ha_rows) 1); |
|
69 |
res= (end_pos < start_pos ? (ha_rows) 0 : |
|
70 |
(end_pos == start_pos ? (ha_rows) 1 : end_pos-start_pos)); |
|
71 |
if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR) |
|
72 |
res=HA_POS_ERROR; |
|
73 |
}
|
|
74 |
||
75 |
if (info->s->concurrent_insert) |
|
76 |
rw_unlock(&info->s->key_root_lock[inx]); |
|
77 |
fast_mi_writeinfo(info); |
|
78 |
||
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
79 |
return(res); |
1
by brian
clean slate |
80 |
}
|
81 |
||
82 |
||
83 |
/* Find relative position (in records) for key in index-tree */
|
|
84 |
||
85 |
static ha_rows _mi_record_pos(MI_INFO *info, const uchar *key, |
|
86 |
key_part_map keypart_map, |
|
87 |
enum ha_rkey_function search_flag) |
|
88 |
{
|
|
89 |
uint inx=(uint) info->lastinx, nextflag, key_len; |
|
90 |
MI_KEYDEF *keyinfo=info->s->keyinfo+inx; |
|
91 |
uchar *key_buff; |
|
92 |
double pos; |
|
93 |
||
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
94 |
assert(keypart_map); |
1
by brian
clean slate |
95 |
|
96 |
key_buff=info->lastkey+info->s->base.max_key_length; |
|
97 |
key_len=_mi_pack_key(info,inx,key_buff,(uchar*) key, keypart_map, |
|
98 |
(HA_KEYSEG**) 0); |
|
99 |
nextflag=myisam_read_vec[search_flag]; |
|
100 |
if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST))) |
|
101 |
key_len=USE_WHOLE_KEY; |
|
102 |
||
103 |
/*
|
|
104 |
my_handler.c:ha_compare_text() has a flag 'skip_end_space'.
|
|
105 |
This is set in my_handler.c:ha_key_cmp() in dependence on the
|
|
106 |
compare flags 'nextflag' and the column type.
|
|
107 |
||
108 |
TEXT columns are of type HA_KEYTYPE_VARTEXT. In this case the
|
|
109 |
condition is skip_end_space= ((nextflag & (SEARCH_FIND |
|
|
110 |
SEARCH_UPDATE)) == SEARCH_FIND).
|
|
111 |
||
112 |
SEARCH_FIND is used for an exact key search. The combination
|
|
113 |
SEARCH_FIND | SEARCH_UPDATE is used in write/update/delete
|
|
114 |
operations with a comment like "Not real duplicates", whatever this
|
|
115 |
means. From the condition above we can see that 'skip_end_space' is
|
|
116 |
always false for these operations. The result is that trailing space
|
|
117 |
counts in key comparison and hence, emtpy strings ('', string length
|
|
118 |
zero, but not NULL) compare less that strings starting with control
|
|
119 |
characters and these in turn compare less than strings starting with
|
|
120 |
blanks.
|
|
121 |
||
122 |
When estimating the number of records in a key range, we request an
|
|
123 |
exact search for the minimum key. This translates into a plain
|
|
124 |
SEARCH_FIND flag. Using this alone would lead to a 'skip_end_space'
|
|
125 |
compare. Empty strings would be expected above control characters.
|
|
126 |
Their keys would not be found because they are located below control
|
|
127 |
characters.
|
|
128 |
||
129 |
This is the reason that we add the SEARCH_UPDATE flag here. It makes
|
|
130 |
the key estimation compare in the same way like key write operations
|
|
131 |
do. Olny so we will find the keys where they have been inserted.
|
|
132 |
||
133 |
Adding the flag unconditionally does not hurt as it is used in the
|
|
134 |
above mentioned condition only. So it can safely be used together
|
|
135 |
with other flags.
|
|
136 |
*/
|
|
137 |
pos=_mi_search_pos(info,keyinfo,key_buff,key_len, |
|
138 |
nextflag | SEARCH_SAVE_BUFF | SEARCH_UPDATE, |
|
139 |
info->s->state.key_root[inx]); |
|
140 |
if (pos >= 0.0) |
|
141 |
{
|
|
313
by Brian Aker
Further work with ulong in myisam |
142 |
return((uint32_t) (pos*info->state->records+0.5)); |
1
by brian
clean slate |
143 |
}
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
144 |
return(HA_POS_ERROR); |
1
by brian
clean slate |
145 |
}
|
146 |
||
147 |
||
148 |
/* This is a modified version of _mi_search */
|
|
149 |
/* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
|
|
150 |
||
151 |
static double _mi_search_pos(register MI_INFO *info, |
|
152 |
register MI_KEYDEF *keyinfo, |
|
153 |
uchar *key, uint key_len, uint nextflag, |
|
154 |
register my_off_t pos) |
|
155 |
{
|
|
156 |
int flag; |
|
157 |
uint nod_flag, keynr, max_keynr= 0; |
|
281
by Brian Aker
Converted myisam away from my_bool |
158 |
bool after_key; |
1
by brian
clean slate |
159 |
uchar *keypos,*buff; |
160 |
double offset; |
|
161 |
||
162 |
if (pos == HA_OFFSET_ERROR) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
163 |
return(0.5); |
1
by brian
clean slate |
164 |
|
165 |
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1))) |
|
166 |
goto err; |
|
167 |
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag, |
|
168 |
&keypos,info->lastkey, &after_key); |
|
169 |
nod_flag=mi_test_if_nod(buff); |
|
170 |
keynr=_mi_keynr(info,keyinfo,buff,keypos,&max_keynr); |
|
171 |
||
172 |
if (flag) |
|
173 |
{
|
|
174 |
if (flag == MI_FOUND_WRONG_KEY) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
175 |
return(-1); /* error */ |
1
by brian
clean slate |
176 |
/*
|
177 |
Didn't found match. keypos points at next (bigger) key
|
|
178 |
Try to find a smaller, better matching key.
|
|
179 |
Matches keynr + [0-1]
|
|
180 |
*/
|
|
181 |
if (flag > 0 && ! nod_flag) |
|
182 |
offset= 1.0; |
|
183 |
else if ((offset=_mi_search_pos(info,keyinfo,key,key_len,nextflag, |
|
184 |
_mi_kpos(nod_flag,keypos))) < 0) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
185 |
return(offset); |
1
by brian
clean slate |
186 |
}
|
187 |
else
|
|
188 |
{
|
|
189 |
/*
|
|
190 |
Found match. Keypos points at the start of the found key
|
|
191 |
Matches keynr+1
|
|
192 |
*/
|
|
193 |
offset=1.0; /* Matches keynr+1 */ |
|
194 |
if ((nextflag & SEARCH_FIND) && nod_flag && |
|
195 |
((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME || |
|
196 |
key_len != USE_WHOLE_KEY)) |
|
197 |
{
|
|
198 |
/*
|
|
199 |
There may be identical keys in the tree. Try to match on of those.
|
|
200 |
Matches keynr + [0-1]
|
|
201 |
*/
|
|
202 |
if ((offset=_mi_search_pos(info,keyinfo,key,key_len,SEARCH_FIND, |
|
203 |
_mi_kpos(nod_flag,keypos))) < 0) |
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
204 |
return(offset); /* Read error */ |
1
by brian
clean slate |
205 |
}
|
206 |
}
|
|
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
207 |
return((keynr+offset)/(max_keynr+1)); |
1
by brian
clean slate |
208 |
err: |
51.1.108
by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE |
209 |
return (-1.0); |
1
by brian
clean slate |
210 |
}
|
211 |
||
212 |
||
213 |
/* Get keynummer of current key and max number of keys in nod */
|
|
214 |
||
215 |
static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, |
|
216 |
uchar *keypos, uint *ret_max_key) |
|
217 |
{
|
|
218 |
uint nod_flag,keynr,max_key; |
|
219 |
uchar t_buff[MI_MAX_KEY_BUFF],*end; |
|
220 |
||
221 |
end= page+mi_getint(page); |
|
222 |
nod_flag=mi_test_if_nod(page); |
|
223 |
page+=2+nod_flag; |
|
224 |
||
225 |
if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY))) |
|
226 |
{
|
|
227 |
*ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag); |
|
228 |
return (uint) (keypos-page)/(keyinfo->keylength+nod_flag); |
|
229 |
}
|
|
230 |
||
231 |
max_key=keynr=0; |
|
232 |
t_buff[0]=0; /* Safety */ |
|
233 |
while (page < end) |
|
234 |
{
|
|
235 |
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff)) |
|
236 |
return 0; /* Error */ |
|
237 |
max_key++; |
|
238 |
if (page == keypos) |
|
239 |
keynr=max_key; |
|
240 |
}
|
|
241 |
*ret_max_key=max_key; |
|
242 |
return(keynr); |
|
243 |
}
|