/*
Copyright ( c ) 2000 , 2011 , Oracle and / or its affiliates
Copyright ( c ) 2010 , 2020 , MariaDB Corporation .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/*
Gives a approximated number of how many records there is between two keys .
Used when optimizing querries .
*/
#include "myisamdef.h"
#include "rt_index.h"
static double _mi_record_pos(MI_INFO *, const uchar *, key_part_map,
enum ha_rkey_function, ulonglong *);
static double _mi_search_pos(MI_INFO *,MI_KEYDEF *,uchar *, uint,uint,
my_off_t,my_bool, ulonglong *);
static uint _mi_keynr(MI_INFO *info,MI_KEYDEF *,uchar *, uchar *,uint *);
/*
Estimate how many records there is in a given range
SYNOPSIS
mi_records_in_range ( )
info MyISAM handler
inx Index to use
min_key Min key . Is = 0 if no min range
max_key Max key . Is = 0 if no max range
NOTES
We should ONLY return 0 if there is no rows in range
RETURN
HA_POS_ERROR error ( or we can ' t estimate number of rows )
number Estimated number of rows
*/
ha_rows mi_records_in_range(MI_INFO *info, int inx,
const key_range *min_key, const key_range *max_key,
page_range *pages)
{
ha_rows res;
double start_pos,end_pos,diff;
DBUG_ENTER("mi_records_in_range" );
if ((inx = _mi_check_index(info,inx)) < 0 )
DBUG_RETURN(HA_POS_ERROR);
if (fast_mi_readinfo(info))
DBUG_RETURN(HA_POS_ERROR);
info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED);
if (info->s->concurrent_insert)
mysql_rwlock_rdlock(&info->s->key_root_lock[inx]);
switch (info->s->keyinfo[inx].key_alg){
case HA_KEY_ALG_RTREE:
{
uchar * key_buff;
uint start_key_len;
/*
The problem is that the optimizer doesn ' t support
RTree keys properly at the moment .
Hope this will be fixed some day .
But now NULL in the min_key means that we
didn ' t make the task for the RTree key
and expect BTree functionality from it .
As it ' s not able to handle such request
we return the error .
*/
if (!min_key)
{
res= HA_POS_ERROR;
break ;
}
key_buff= info->lastkey+info->s->base.max_key_length;
start_key_len= _mi_pack_key(info,inx, key_buff,
(uchar*) min_key->key, min_key->keypart_map,
(HA_KEYSEG**) 0 );
res= rtree_estimate(info, inx, key_buff, start_key_len,
myisam_read_vec[min_key->flag]);
res= res ? res : 1 ; /* Don't return 0 */
break ;
}
case HA_KEY_ALG_BTREE:
default :
start_pos= (min_key ?_mi_record_pos(info, min_key->key,
min_key->keypart_map, min_key->flag,
&pages->first_page)
: (double ) 0 );
end_pos= (max_key ? _mi_record_pos(info, max_key->key,
max_key->keypart_map, max_key->flag,
&pages->last_page)
: (double ) info->state->records);
res= (end_pos < start_pos ? (ha_rows) 0 :
(end_pos == start_pos ? (ha_rows) 1 : (ha_rows) (end_pos-start_pos)));
if (start_pos == (double ) HA_POS_ERROR || end_pos == (double ) HA_POS_ERROR)
res=HA_POS_ERROR;
else
{
diff= end_pos - start_pos;
if (diff >= 0 )
{
if (!(res= (ha_rows) (diff + 0 .5 )))
res= 1 ;
}
else
res= 0 ;
}
}
if (info->s->concurrent_insert)
mysql_rwlock_unlock(&info->s->key_root_lock[inx]);
fast_mi_writeinfo(info);
DBUG_PRINT("info" ,("records: %ld" ,(ulong) (res)));
DBUG_RETURN(res);
}
/*
To find an approximate relative position of a key tuple among all index
key tuples would not be hard if we considered B - trees where all key
tuples were contained only in leaf nodes . If we consider a B - tree where
key tuples are stored also in non - leaf nodes we have to convert such
tree into the tree of the first type . The transformation procedure is
simple : the key tuple k goes alter the last key tuple in the most right
sub - tree pointer to which is coupled with k . As a result of this
transformation each leaf node except the most right one in the tree will
contain one extra key tuple following those originally belonging to
the leaf .
*/
/* Find relative position (in records) for key in index-tree */
static double _mi_record_pos(MI_INFO *info, const uchar *key,
key_part_map keypart_map,
enum ha_rkey_function search_flag,
ulonglong *final_page)
{
uint inx=(uint) info->lastinx, nextflag, key_len;
MI_KEYDEF *keyinfo=info->s->keyinfo+inx;
uchar *key_buff;
double pos;
DBUG_ENTER("_mi_record_pos" );
DBUG_PRINT("enter" ,("search_flag: %d" ,search_flag));
DBUG_ASSERT(keypart_map);
key_buff=info->lastkey+info->s->base.max_key_length;
key_len=_mi_pack_key(info,inx,key_buff,(uchar*) key, keypart_map,
(HA_KEYSEG**) 0 );
DBUG_EXECUTE("key" ,_mi_print_key(DBUG_FILE,keyinfo->seg,
(uchar*) key_buff,key_len););
nextflag=myisam_read_vec[search_flag];
if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
key_len=USE_WHOLE_KEY;
/*
my_handler . c : ha_compare_text ( ) has a flag ' skip_end_space ' .
This is set in my_handler . c : ha_key_cmp ( ) in dependence on the
compare flags ' nextflag ' and the column type .
TEXT columns are of type HA_KEYTYPE_VARTEXT . In this case the
condition is skip_end_space = ( ( nextflag & ( SEARCH_FIND |
SEARCH_UPDATE ) ) = = SEARCH_FIND ) .
SEARCH_FIND is used for an exact key search . The combination
SEARCH_FIND | SEARCH_UPDATE is used in write / update / delete
operations with a comment like " Not real duplicates " , whatever this
means . From the condition above we can see that ' skip_end_space ' is
always false for these operations . The result is that trailing space
counts in key comparison and hence , empty strings ( ' ' , string length
zero , but not NULL ) compare less that strings starting with control
characters and these in turn compare less than strings starting with
blanks .
When estimating the number of records in a key range , we request an
exact search for the minimum key . This translates into a plain
SEARCH_FIND flag . Using this alone would lead to a ' skip_end_space '
compare . Empty strings would be expected above control characters .
Their keys would not be found because they are located below control
characters .
This is the reason that we add the SEARCH_UPDATE flag here . It makes
the key estimation compare in the same way like key write operations
do . Only so we will find the keys where they have been inserted .
Adding the flag unconditionally does not hurt as it is used in the
above mentioned condition only . So it can safely be used together
with other flags .
*/
pos=_mi_search_pos(info,keyinfo,key_buff,key_len,
nextflag | SEARCH_SAVE_BUFF | SEARCH_UPDATE,
info->s->state.key_root[inx], TRUE ,
final_page);
if (pos >= 0 .0 )
{
DBUG_PRINT("exit" ,("pos: %g" ,(pos*info->state->records)));
DBUG_RETURN(pos*info->state->records);
}
DBUG_RETURN((double ) (HA_POS_ERROR));
}
/* This is a modified version of _mi_search */
/* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
static double _mi_search_pos(register MI_INFO *info,
register MI_KEYDEF *keyinfo,
uchar *key, uint key_len, uint nextflag,
register my_off_t pos, my_bool last_in_level,
ulonglong *final_page)
{
int flag;
uint nod_flag,keynr,UNINIT_VAR(max_keynr);
my_bool after_key;
uchar *keypos,*buff;
double offset;
DBUG_ENTER("_mi_search_pos" );
if (pos == HA_OFFSET_ERROR)
DBUG_RETURN(0 .5 );
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1 )))
goto err;
*final_page= pos;
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
&keypos,info->lastkey, &after_key);
nod_flag=mi_test_if_nod(buff);
keynr=_mi_keynr(info,keyinfo,buff,keypos,&max_keynr);
if (flag)
{
if (flag == MI_FOUND_WRONG_KEY)
DBUG_RETURN(-1 ); /* error */
/*
Didn ' t found match . keypos points at next ( bigger ) key
Try to find a smaller , better matching key .
Matches keynr + [ 0 - 1 ]
*/
if (flag > 0 && ! nod_flag)
offset= 1 .0 ;
else if ((offset=_mi_search_pos(info,keyinfo,key,key_len,nextflag,
_mi_kpos(nod_flag,keypos),
last_in_level && after_key,
final_page)) < 0 )
DBUG_RETURN(offset);
}
else
{
/*
Found match . Keypos points at the start of the found key
Matches keynr + 1
*/
offset=1 .0 ; /* Matches keynr+1 */
if ((nextflag & SEARCH_FIND) && nod_flag &&
((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME ||
key_len != USE_WHOLE_KEY))
{
/*
There may be identical keys in the tree . Try to match on of those .
Matches keynr + [ 0 - 1 ]
*/
if ((offset=_mi_search_pos(info,keyinfo,key,key_len,SEARCH_FIND,
_mi_kpos(nod_flag,keypos),
last_in_level && after_key,
final_page)) < 0 )
DBUG_RETURN(offset); /* Read error */
}
}
DBUG_PRINT("info" ,("keynr: %d offset: %g max_keynr: %d nod: %d flag: %d" ,
keynr,offset,max_keynr,nod_flag,flag));
DBUG_RETURN((keynr+offset-MY_TEST(!nod_flag))/
(max_keynr+MY_TEST(nod_flag || !last_in_level)));
err:
DBUG_PRINT("exit" ,("Error: %d" ,my_errno));
DBUG_RETURN (-1 .0 );
}
/* Get keynumber of current key and max number of keys in nod */
static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
uchar *keypos, uint *ret_max_key)
{
uint nod_flag,keynr,max_key;
uchar t_buff[HA_MAX_KEY_BUFF],*end;
end= page+mi_getint(page);
nod_flag=mi_test_if_nod(page);
page+=2 +nod_flag;
if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
{
*ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag);
return (uint) (keypos-page)/(keyinfo->keylength+nod_flag);
}
max_key=keynr=0 ;
t_buff[0 ]=0 ; /* Safety */
while (page < end)
{
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff))
return 0 ; /* Error */
max_key++;
if (page == keypos)
keynr=max_key;
}
*ret_max_key=max_key;
return (keynr);
}
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.70 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland