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 |
DBUG_ENTER("mi_records_in_range"); |
|
51 |
||
52 |
if ((inx = _mi_check_index(info,inx)) < 0) |
|
53 |
DBUG_RETURN(HA_POS_ERROR); |
|
54 |
||
55 |
if (fast_mi_readinfo(info)) |
|
56 |
DBUG_RETURN(HA_POS_ERROR); |
|
57 |
info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED); |
|
58 |
if (info->s->concurrent_insert) |
|
59 |
rw_rdlock(&info->s->key_root_lock[inx]); |
|
60 |
||
61 |
switch(info->s->keyinfo[inx].key_alg){ |
|
62 |
case HA_KEY_ALG_BTREE: |
|
63 |
default: |
|
64 |
start_pos= (min_key ? _mi_record_pos(info, min_key->key, |
|
65 |
min_key->keypart_map, min_key->flag) |
|
66 |
: (ha_rows) 0); |
|
67 |
end_pos= (max_key ? _mi_record_pos(info, max_key->key, |
|
68 |
max_key->keypart_map, max_key->flag) |
|
69 |
: info->state->records + (ha_rows) 1); |
|
70 |
res= (end_pos < start_pos ? (ha_rows) 0 : |
|
71 |
(end_pos == start_pos ? (ha_rows) 1 : end_pos-start_pos)); |
|
72 |
if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR) |
|
73 |
res=HA_POS_ERROR; |
|
74 |
}
|
|
75 |
||
76 |
if (info->s->concurrent_insert) |
|
77 |
rw_unlock(&info->s->key_root_lock[inx]); |
|
78 |
fast_mi_writeinfo(info); |
|
79 |
||
80 |
DBUG_PRINT("info",("records: %ld",(ulong) (res))); |
|
81 |
DBUG_RETURN(res); |
|
82 |
}
|
|
83 |
||
84 |
||
85 |
/* Find relative position (in records) for key in index-tree */
|
|
86 |
||
87 |
static ha_rows _mi_record_pos(MI_INFO *info, const uchar *key, |
|
88 |
key_part_map keypart_map, |
|
89 |
enum ha_rkey_function search_flag) |
|
90 |
{
|
|
91 |
uint inx=(uint) info->lastinx, nextflag, key_len; |
|
92 |
MI_KEYDEF *keyinfo=info->s->keyinfo+inx; |
|
93 |
uchar *key_buff; |
|
94 |
double pos; |
|
95 |
||
96 |
DBUG_ENTER("_mi_record_pos"); |
|
97 |
DBUG_PRINT("enter",("search_flag: %d",search_flag)); |
|
98 |
DBUG_ASSERT(keypart_map); |
|
99 |
||
100 |
key_buff=info->lastkey+info->s->base.max_key_length; |
|
101 |
key_len=_mi_pack_key(info,inx,key_buff,(uchar*) key, keypart_map, |
|
102 |
(HA_KEYSEG**) 0); |
|
103 |
DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg, |
|
104 |
(uchar*) key_buff,key_len);); |
|
105 |
nextflag=myisam_read_vec[search_flag]; |
|
106 |
if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST))) |
|
107 |
key_len=USE_WHOLE_KEY; |
|
108 |
||
109 |
/*
|
|
110 |
my_handler.c:ha_compare_text() has a flag 'skip_end_space'.
|
|
111 |
This is set in my_handler.c:ha_key_cmp() in dependence on the
|
|
112 |
compare flags 'nextflag' and the column type.
|
|
113 |
||
114 |
TEXT columns are of type HA_KEYTYPE_VARTEXT. In this case the
|
|
115 |
condition is skip_end_space= ((nextflag & (SEARCH_FIND |
|
|
116 |
SEARCH_UPDATE)) == SEARCH_FIND).
|
|
117 |
||
118 |
SEARCH_FIND is used for an exact key search. The combination
|
|
119 |
SEARCH_FIND | SEARCH_UPDATE is used in write/update/delete
|
|
120 |
operations with a comment like "Not real duplicates", whatever this
|
|
121 |
means. From the condition above we can see that 'skip_end_space' is
|
|
122 |
always false for these operations. The result is that trailing space
|
|
123 |
counts in key comparison and hence, emtpy strings ('', string length
|
|
124 |
zero, but not NULL) compare less that strings starting with control
|
|
125 |
characters and these in turn compare less than strings starting with
|
|
126 |
blanks.
|
|
127 |
||
128 |
When estimating the number of records in a key range, we request an
|
|
129 |
exact search for the minimum key. This translates into a plain
|
|
130 |
SEARCH_FIND flag. Using this alone would lead to a 'skip_end_space'
|
|
131 |
compare. Empty strings would be expected above control characters.
|
|
132 |
Their keys would not be found because they are located below control
|
|
133 |
characters.
|
|
134 |
||
135 |
This is the reason that we add the SEARCH_UPDATE flag here. It makes
|
|
136 |
the key estimation compare in the same way like key write operations
|
|
137 |
do. Olny so we will find the keys where they have been inserted.
|
|
138 |
||
139 |
Adding the flag unconditionally does not hurt as it is used in the
|
|
140 |
above mentioned condition only. So it can safely be used together
|
|
141 |
with other flags.
|
|
142 |
*/
|
|
143 |
pos=_mi_search_pos(info,keyinfo,key_buff,key_len, |
|
144 |
nextflag | SEARCH_SAVE_BUFF | SEARCH_UPDATE, |
|
145 |
info->s->state.key_root[inx]); |
|
146 |
if (pos >= 0.0) |
|
147 |
{
|
|
148 |
DBUG_PRINT("exit",("pos: %ld",(ulong) (pos*info->state->records))); |
|
149 |
DBUG_RETURN((ulong) (pos*info->state->records+0.5)); |
|
150 |
}
|
|
151 |
DBUG_RETURN(HA_POS_ERROR); |
|
152 |
}
|
|
153 |
||
154 |
||
155 |
/* This is a modified version of _mi_search */
|
|
156 |
/* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
|
|
157 |
||
158 |
static double _mi_search_pos(register MI_INFO *info, |
|
159 |
register MI_KEYDEF *keyinfo, |
|
160 |
uchar *key, uint key_len, uint nextflag, |
|
161 |
register my_off_t pos) |
|
162 |
{
|
|
163 |
int flag; |
|
164 |
uint nod_flag, keynr, max_keynr= 0; |
|
165 |
my_bool after_key; |
|
166 |
uchar *keypos,*buff; |
|
167 |
double offset; |
|
168 |
DBUG_ENTER("_mi_search_pos"); |
|
169 |
||
170 |
if (pos == HA_OFFSET_ERROR) |
|
171 |
DBUG_RETURN(0.5); |
|
172 |
||
173 |
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1))) |
|
174 |
goto err; |
|
175 |
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag, |
|
176 |
&keypos,info->lastkey, &after_key); |
|
177 |
nod_flag=mi_test_if_nod(buff); |
|
178 |
keynr=_mi_keynr(info,keyinfo,buff,keypos,&max_keynr); |
|
179 |
||
180 |
if (flag) |
|
181 |
{
|
|
182 |
if (flag == MI_FOUND_WRONG_KEY) |
|
183 |
DBUG_RETURN(-1); /* error */ |
|
184 |
/*
|
|
185 |
Didn't found match. keypos points at next (bigger) key
|
|
186 |
Try to find a smaller, better matching key.
|
|
187 |
Matches keynr + [0-1]
|
|
188 |
*/
|
|
189 |
if (flag > 0 && ! nod_flag) |
|
190 |
offset= 1.0; |
|
191 |
else if ((offset=_mi_search_pos(info,keyinfo,key,key_len,nextflag, |
|
192 |
_mi_kpos(nod_flag,keypos))) < 0) |
|
193 |
DBUG_RETURN(offset); |
|
194 |
}
|
|
195 |
else
|
|
196 |
{
|
|
197 |
/*
|
|
198 |
Found match. Keypos points at the start of the found key
|
|
199 |
Matches keynr+1
|
|
200 |
*/
|
|
201 |
offset=1.0; /* Matches keynr+1 */ |
|
202 |
if ((nextflag & SEARCH_FIND) && nod_flag && |
|
203 |
((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME || |
|
204 |
key_len != USE_WHOLE_KEY)) |
|
205 |
{
|
|
206 |
/*
|
|
207 |
There may be identical keys in the tree. Try to match on of those.
|
|
208 |
Matches keynr + [0-1]
|
|
209 |
*/
|
|
210 |
if ((offset=_mi_search_pos(info,keyinfo,key,key_len,SEARCH_FIND, |
|
211 |
_mi_kpos(nod_flag,keypos))) < 0) |
|
212 |
DBUG_RETURN(offset); /* Read error */ |
|
213 |
}
|
|
214 |
}
|
|
215 |
DBUG_PRINT("info",("keynr: %d offset: %g max_keynr: %d nod: %d flag: %d", |
|
216 |
keynr,offset,max_keynr,nod_flag,flag)); |
|
217 |
DBUG_RETURN((keynr+offset)/(max_keynr+1)); |
|
218 |
err: |
|
219 |
DBUG_PRINT("exit",("Error: %d",my_errno)); |
|
220 |
DBUG_RETURN (-1.0); |
|
221 |
}
|
|
222 |
||
223 |
||
224 |
/* Get keynummer of current key and max number of keys in nod */
|
|
225 |
||
226 |
static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, |
|
227 |
uchar *keypos, uint *ret_max_key) |
|
228 |
{
|
|
229 |
uint nod_flag,keynr,max_key; |
|
230 |
uchar t_buff[MI_MAX_KEY_BUFF],*end; |
|
231 |
||
232 |
end= page+mi_getint(page); |
|
233 |
nod_flag=mi_test_if_nod(page); |
|
234 |
page+=2+nod_flag; |
|
235 |
||
236 |
if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY))) |
|
237 |
{
|
|
238 |
*ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag); |
|
239 |
return (uint) (keypos-page)/(keyinfo->keylength+nod_flag); |
|
240 |
}
|
|
241 |
||
242 |
max_key=keynr=0; |
|
243 |
t_buff[0]=0; /* Safety */ |
|
244 |
while (page < end) |
|
245 |
{
|
|
246 |
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff)) |
|
247 |
return 0; /* Error */ |
|
248 |
max_key++; |
|
249 |
if (page == keypos) |
|
250 |
keynr=max_key; |
|
251 |
}
|
|
252 |
*ret_max_key=max_key; |
|
253 |
return(keynr); |
|
254 |
}
|