1
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
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.
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.
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 */
17
Gives a approximated number of how many records there is between two keys.
18
Used when optimizing querries.
21
#include "maria_def.h"
22
#include "ma_rt_index.h"
24
static ha_rows _ma_record_pos(MARIA_HA *,const uchar *, key_part_map,
25
enum ha_rkey_function);
26
static double _ma_search_pos(MARIA_HA *, MARIA_KEY *, uint32, my_off_t);
27
static uint _ma_keynr(MARIA_HA *, MARIA_KEYDEF *, uchar *, uchar *, uint *);
31
@brief Estimate how many records there is in a given range
33
@param info MARIA handler
34
@param inx Index to use
35
@param min_key Min key. Is = 0 if no min range
36
@param max_key Max key. Is = 0 if no max range
39
We should ONLY return 0 if there is no rows in range
41
@return Estimated number of rows or error
42
@retval HA_POS_ERROR error (or we can't estimate number of rows)
43
@retval number Estimated number of rows
46
ha_rows maria_records_in_range(MARIA_HA *info, int inx, key_range *min_key,
49
ha_rows start_pos,end_pos,res;
50
MARIA_SHARE *share= info->s;
52
MARIA_KEYDEF *keyinfo;
53
DBUG_ENTER("maria_records_in_range");
55
if ((inx = _ma_check_index(info,inx)) < 0)
56
DBUG_RETURN(HA_POS_ERROR);
58
if (fast_ma_readinfo(info))
59
DBUG_RETURN(HA_POS_ERROR);
60
info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED);
61
keyinfo= share->keyinfo + inx;
62
if (share->lock_key_trees)
63
rw_rdlock(&keyinfo->root_lock);
65
switch (keyinfo->key_alg) {
66
#ifdef HAVE_RTREE_KEYS
67
case HA_KEY_ALG_RTREE:
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
86
key_buff= info->last_key.data + share->base.max_key_length;
87
_ma_pack_key(info, &key, inx, key_buff,
88
min_key->key, min_key->keypart_map,
90
res= maria_rtree_estimate(info, &key, maria_read_vec[min_key->flag]);
91
res= res ? res : 1; /* Don't return 0 */
95
case HA_KEY_ALG_BTREE:
98
_ma_record_pos(info, min_key->key, min_key->keypart_map,
102
_ma_record_pos(info, max_key->key, max_key->keypart_map,
104
info->state->records + (ha_rows) 1);
105
res= (end_pos < start_pos ? (ha_rows) 0 :
106
(end_pos == start_pos ? (ha_rows) 1 : end_pos-start_pos));
107
if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR)
111
if (share->lock_key_trees)
112
rw_unlock(&keyinfo->root_lock);
113
fast_ma_writeinfo(info);
117
If res==0 (no rows), if we need to guarantee repeatability of the search,
118
we will need to set a next-key lock in this statement.
119
Also SELECT COUNT(*)...
122
DBUG_PRINT("info",("records: %ld",(ulong) (res)));
127
/* Find relative position (in records) for key in index-tree */
129
static ha_rows _ma_record_pos(MARIA_HA *info, const uchar *key_data,
130
key_part_map keypart_map,
131
enum ha_rkey_function search_flag)
133
uint inx= (uint) info->lastinx;
138
DBUG_ENTER("_ma_record_pos");
139
DBUG_PRINT("enter",("search_flag: %d",search_flag));
140
DBUG_ASSERT(keypart_map);
142
key_buff= info->lastkey_buff+info->s->base.max_key_length;
143
_ma_pack_key(info, &key, inx, key_buff, key_data, keypart_map,
145
DBUG_EXECUTE("key", _ma_print_key(DBUG_FILE, &key););
146
nextflag=maria_read_vec[search_flag];
149
my_handler.c:ha_compare_text() has a flag 'skip_end_space'.
150
This is set in my_handler.c:ha_key_cmp() in dependence on the
151
compare flags 'nextflag' and the column type.
153
TEXT columns are of type HA_KEYTYPE_VARTEXT. In this case the
154
condition is skip_end_space= ((nextflag & (SEARCH_FIND |
155
SEARCH_UPDATE)) == SEARCH_FIND).
157
SEARCH_FIND is used for an exact key search. The combination
158
SEARCH_FIND | SEARCH_UPDATE is used in write/update/delete
159
operations with a comment like "Not real duplicates", whatever this
160
means. From the condition above we can see that 'skip_end_space' is
161
always false for these operations. The result is that trailing space
162
counts in key comparison and hence, emtpy strings ('', string length
163
zero, but not NULL) compare less that strings starting with control
164
characters and these in turn compare less than strings starting with
167
When estimating the number of records in a key range, we request an
168
exact search for the minimum key. This translates into a plain
169
SEARCH_FIND flag. Using this alone would lead to a 'skip_end_space'
170
compare. Empty strings would be expected above control characters.
171
Their keys would not be found because they are located below control
174
This is the reason that we add the SEARCH_UPDATE flag here. It makes
175
the key estimation compare in the same way like key write operations
176
do. Olny so we will find the keys where they have been inserted.
178
Adding the flag unconditionally does not hurt as it is used in the
179
above mentioned condition only. So it can safely be used together
182
pos= _ma_search_pos(info, &key,
183
nextflag | SEARCH_SAVE_BUFF | SEARCH_UPDATE,
184
info->s->state.key_root[inx]);
187
DBUG_PRINT("exit",("pos: %ld",(ulong) (pos*info->state->records)));
188
DBUG_RETURN((ulong) (pos*info->state->records+0.5));
190
DBUG_RETURN(HA_POS_ERROR);
195
Find offset for key on index page
198
Modified version of _ma_search()
201
@retval 0.0 <= x <= 1.0
204
static double _ma_search_pos(MARIA_HA *info, MARIA_KEY *key,
205
uint32 nextflag, my_off_t pos)
208
uint nod_flag,keynr,max_keynr;
210
uchar *keypos, *buff;
212
MARIA_KEYDEF *keyinfo= key->keyinfo;
213
DBUG_ENTER("_ma_search_pos");
214
LINT_INIT(max_keynr);
216
if (pos == HA_OFFSET_ERROR)
219
if (!(buff= _ma_fetch_keypage(info,keyinfo, pos,
220
PAGECACHE_LOCK_LEFT_UNLOCKED, DFLT_INIT_HITS,
223
flag= (*keyinfo->bin_search)(key, buff, nextflag, &keypos,
224
info->lastkey_buff, &after_key);
225
nod_flag=_ma_test_if_nod(info->s, buff);
226
keynr= _ma_keynr(info,keyinfo,buff,keypos,&max_keynr);
230
if (flag == MARIA_FOUND_WRONG_KEY)
231
DBUG_RETURN(-1); /* error */
233
Didn't found match. keypos points at next (bigger) key
234
Try to find a smaller, better matching key.
235
Matches keynr + [0-1]
237
if (flag > 0 && ! nod_flag)
239
else if ((offset= _ma_search_pos(info, key, nextflag,
240
_ma_kpos(nod_flag,keypos))) < 0)
246
Found match. Keypos points at the start of the found key
249
offset=1.0; /* Matches keynr+1 */
250
if ((nextflag & SEARCH_FIND) && nod_flag &&
251
((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME ||
252
(nextflag & (SEARCH_PREFIX | SEARCH_NO_FIND | SEARCH_LAST |
256
There may be identical keys in the tree. Try to match on of those.
257
Matches keynr + [0-1]
259
if ((offset= _ma_search_pos(info, key, SEARCH_FIND,
260
_ma_kpos(nod_flag,keypos))) < 0)
261
DBUG_RETURN(offset); /* Read error */
264
DBUG_PRINT("info",("keynr: %d offset: %g max_keynr: %d nod: %d flag: %d",
265
keynr,offset,max_keynr,nod_flag,flag));
266
DBUG_RETURN((keynr+offset)/(max_keynr+1));
268
DBUG_PRINT("exit",("Error: %d",my_errno));
273
/* Get keynummer of current key and max number of keys in nod */
275
static uint _ma_keynr(MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
276
uchar *page, uchar *keypos, uint *ret_max_key)
278
uint page_flag, nod_flag, used_length, keynr, max_key;
279
uchar t_buff[MARIA_MAX_KEY_BUFF],*end;
282
page_flag= _ma_get_keypage_flag(info->s, page);
283
_ma_get_used_and_nod_with_flag(info->s, page_flag, page, used_length,
285
end= page+ used_length;
286
page+= info->s->keypage_header + nod_flag;
288
if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)) &&
289
! (page_flag & KEYPAGE_FLAG_HAS_TRANSID))
291
*ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag);
292
return (uint) (keypos-page)/(keyinfo->keylength+nod_flag);
296
t_buff[0]=0; /* Safety */
298
key.keyinfo= keyinfo;
302
if (!(page= (*keyinfo->skip_key)(&key, page_flag, nod_flag, page)))
305
return 0; /* Error */
311
*ret_max_key=max_key;