~drizzle-trunk/drizzle/development

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
  {
51.1.108 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
142
    return((ulong) (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;
158
  my_bool after_key;
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
}