my_handler.c 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library 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
   Library General Public License for more details.
unknown's avatar
unknown committed
12

13 14 15 16 17 18 19
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA */

#include "my_handler.h"

20
int mi_compare_text(CHARSET_INFO *charset_info, uchar *a, uint a_length,
21 22
		    uchar *b, uint b_length, my_bool part_key,
		    my_bool skip_end_space)
23
{
24 25 26 27
  if (skip_end_space)
    return charset_info->coll->strnncollsp(charset_info, a, a_length,
					   b, b_length);
  return charset_info->coll->strnncoll(charset_info, a, a_length,
28
				       b, b_length, part_key);
29 30
}

31

32
static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length,
33
                       my_bool part_key, my_bool skip_end_space)
34 35 36 37 38 39 40 41 42 43
{
  uint length= min(a_length,b_length);
  uchar *end= a+ length;
  int flag;

  while (a < end)
    if ((flag= (int) *a++ - (int) *b++))
      return flag;
  if (part_key && b_length < a_length)
    return 0;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
  if (skip_end_space && a_length != b_length)
  {
    int swap= 0;
    /*
      We are using space compression. We have to check if longer key
      has next character < ' ', in which case it's less than the shorter
      key that has an implicite space afterwards.

      This code is identical to the one in
      strings/ctype-simple.c:my_strnncollsp_simple
    */
    if (a_length < b_length)
    {
      /* put shorter key in a */
      a_length= b_length;
      a= b;
      swap= -1;					/* swap sign of result */
    }
    for (end= a + a_length-length; a < end ; a++)
    {
      if (*a != ' ')
	return ((int) *a - (int) ' ') ^ swap;
    }
    return 0;
  }
69 70 71 72 73
  return (int) (a_length-b_length);
}


/*
unknown's avatar
unknown committed
74
  Compare two keys
unknown's avatar
unknown committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

  SYNOPSIS
    ha_key_cmp()
    keyseg	Key segments of key to compare
    a		First key to compare, in format from _mi_pack_key()
		This is normally key specified by user
    b		Second key to compare.  This is always from a row
    key_length	Length of key to compare.  This can be shorter than
		a to just compare sub keys
    next_flag	How keys should be compared
		If bit SEARCH_FIND is not set the keys includes the row
		position and this should also be compared

  NOTES
    Number-keys can't be splited
  
  RETURN VALUES
    <0	If a < b
    0	If a == b
    >0	If a > b
95
*/
unknown's avatar
unknown committed
96

unknown's avatar
unknown committed
97 98
#define FCMP(A,B) ((int) (A) - (int) (B))

unknown's avatar
unknown committed
99
int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
unknown's avatar
unknown committed
100 101
	       register uchar *b, uint key_length, uint nextflag,
	       uint *diff_pos)
102 103 104 105 106 107 108
{
  int flag;
  int16 s_1,s_2;
  int32 l_1,l_2;
  uint32 u_1,u_2;
  float f_1,f_2;
  double d_1,d_2;
unknown's avatar
unknown committed
109
  uint next_key_length;
110 111

  *diff_pos=0;
unknown's avatar
unknown committed
112
  for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++)
113 114
  {
    uchar *end;
unknown's avatar
unknown committed
115
    uint piks=! (keyseg->flag & HA_NO_SORT);
116 117 118 119 120 121
    (*diff_pos)++;

    /* Handle NULL part */
    if (keyseg->null_bit)
    {
      key_length--;
unknown's avatar
unknown committed
122
      if (*a != *b && piks)
123 124 125 126 127 128 129 130 131
      {
        flag = (int) *a - (int) *b;
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      }
      b++;
      if (!*a++)                                /* If key was NULL */
      {
        if (nextflag == (SEARCH_FIND | SEARCH_UPDATE))
          nextflag=SEARCH_SAME;                 /* Allow duplicate keys */
unknown's avatar
unknown committed
132 133 134 135 136 137 138 139 140 141
  	else if (nextflag & SEARCH_NULL_ARE_NOT_EQUAL)
	{
	  /*
	    This is only used from mi_check() to calculate cardinality.
	    It can't be used when searching for a key as this would cause
	    compare of (a,b) and (b,a) to return the same value.
	  */
	  return -1;
	}
        next_key_length=key_length;
142 143 144 145
        continue;                               /* To next key part */
      }
    }
    end= a+ min(keyseg->length,key_length);
unknown's avatar
unknown committed
146
    next_key_length=key_length-keyseg->length;
147 148 149 150 151 152 153 154

    switch ((enum ha_base_keytype) keyseg->type) {
    case HA_KEYTYPE_TEXT:                       /* Ascii; Key is converted */
      if (keyseg->flag & HA_SPACE_PACK)
      {
        int a_length,b_length,pack_length;
        get_key_length(a_length,a);
        get_key_pack_length(b_length,pack_length,b);
unknown's avatar
unknown committed
155
        next_key_length=key_length-b_length-pack_length;
156

unknown's avatar
unknown committed
157
        if (piks &&
unknown's avatar
unknown committed
158 159
            (flag=mi_compare_text(keyseg->charset,a,a_length,b,b_length,
				  (my_bool) ((nextflag & SEARCH_PREFIX) &&
160
					     next_key_length <= 0),
unknown's avatar
unknown committed
161
				  (my_bool)!(nextflag & SEARCH_PREFIX))))
162 163 164 165 166 167 168 169
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a+=a_length;
        b+=b_length;
        break;
      }
      else
      {
	uint length=(uint) (end-a), a_length=length, b_length=length;
unknown's avatar
unknown committed
170
        if (piks &&
unknown's avatar
unknown committed
171 172
            (flag= mi_compare_text(keyseg->charset, a, a_length, b, b_length,
				   (my_bool) ((nextflag & SEARCH_PREFIX) &&
173
					      next_key_length <= 0),
unknown's avatar
unknown committed
174
				   (my_bool)!(nextflag & SEARCH_PREFIX))))
175 176 177 178 179 180 181 182 183 184 185
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a=end;
        b+=length;
      }
      break;
    case HA_KEYTYPE_BINARY:
      if (keyseg->flag & HA_SPACE_PACK)
      {
        int a_length,b_length,pack_length;
        get_key_length(a_length,a);
        get_key_pack_length(b_length,pack_length,b);
unknown's avatar
unknown committed
186
        next_key_length=key_length-b_length-pack_length;
187

unknown's avatar
unknown committed
188 189
        if (piks &&
	    (flag=compare_bin(a,a_length,b,b_length,
190
                              (my_bool) ((nextflag & SEARCH_PREFIX) &&
191
                                         next_key_length <= 0),1)))
192 193 194 195 196 197 198 199
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a+=a_length;
        b+=b_length;
        break;
      }
      else
      {
        uint length=keyseg->length;
unknown's avatar
unknown committed
200 201
        if (piks &&
	    (flag=compare_bin(a,length,b,length,
202
                              (my_bool) ((nextflag & SEARCH_PREFIX) &&
203
                                         next_key_length <= 0),0)))
204 205 206 207 208 209 210
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a+=length;
        b+=length;
      }
      break;
    case HA_KEYTYPE_VARTEXT:
      {
unknown's avatar
unknown committed
211
        int a_length,full_a_length,b_length,full_b_length,pack_length;
212 213
        get_key_length(a_length,a);
        get_key_pack_length(b_length,pack_length,b);
unknown's avatar
unknown committed
214 215
        full_a_length= a_length;
        full_b_length= b_length;
unknown's avatar
unknown committed
216
        next_key_length=key_length-b_length-pack_length;
217

unknown's avatar
unknown committed
218 219
        if (piks &&
	    (flag= mi_compare_text(keyseg->charset,a,a_length,b,b_length,
220
                                   (my_bool) ((nextflag & SEARCH_PREFIX) &&
221 222 223 224
                                              next_key_length <= 0),
				   (my_bool) ((nextflag & (SEARCH_FIND |
							   SEARCH_UPDATE)) ==
					      SEARCH_FIND))))
225
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
unknown's avatar
unknown committed
226 227
        a+= full_a_length;
        b+= full_b_length;
228 229 230 231 232 233 234 235
        break;
      }
      break;
    case HA_KEYTYPE_VARBINARY:
      {
        int a_length,b_length,pack_length;
        get_key_length(a_length,a);
        get_key_pack_length(b_length,pack_length,b);
unknown's avatar
unknown committed
236
        next_key_length=key_length-b_length-pack_length;
237

unknown's avatar
unknown committed
238 239
        if (piks &&
	    (flag=compare_bin(a,a_length,b,b_length,
240
                              (my_bool) ((nextflag & SEARCH_PREFIX) &&
241
                                         next_key_length <= 0), 0)))
242 243 244 245 246 247 248 249 250 251
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a+=a_length;
        b+=b_length;
        break;
      }
      break;
    case HA_KEYTYPE_INT8:
    {
      int i_1= (int) *((signed char*) a);
      int i_2= (int) *((signed char*) b);
unknown's avatar
unknown committed
252
      if (piks && (flag = CMP_NUM(i_1,i_2)))
253 254 255 256 257 258 259 260
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a= end;
      b++;
      break;
    }
    case HA_KEYTYPE_SHORT_INT:
      s_1= mi_sint2korr(a);
      s_2= mi_sint2korr(b);
unknown's avatar
unknown committed
261
      if (piks && (flag = CMP_NUM(s_1,s_2)))
262 263 264 265 266 267 268 269 270
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 2; /* sizeof(short int); */
      break;
    case HA_KEYTYPE_USHORT_INT:
      {
        uint16 us_1,us_2;
        us_1= mi_sint2korr(a);
        us_2= mi_sint2korr(b);
unknown's avatar
unknown committed
271
        if (piks && (flag = CMP_NUM(us_1,us_2)))
272 273 274 275 276 277 278 279
          return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
        a=  end;
        b+=2; /* sizeof(short int); */
        break;
      }
    case HA_KEYTYPE_LONG_INT:
      l_1= mi_sint4korr(a);
      l_2= mi_sint4korr(b);
unknown's avatar
unknown committed
280
      if (piks && (flag = CMP_NUM(l_1,l_2)))
281 282 283 284 285 286 287
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 4; /* sizeof(long int); */
      break;
    case HA_KEYTYPE_ULONG_INT:
      u_1= mi_sint4korr(a);
      u_2= mi_sint4korr(b);
unknown's avatar
unknown committed
288
      if (piks && (flag = CMP_NUM(u_1,u_2)))
289 290 291 292 293 294 295
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 4; /* sizeof(long int); */
      break;
    case HA_KEYTYPE_INT24:
      l_1=mi_sint3korr(a);
      l_2=mi_sint3korr(b);
unknown's avatar
unknown committed
296
      if (piks && (flag = CMP_NUM(l_1,l_2)))
297 298 299 300 301 302 303
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 3;
      break;
    case HA_KEYTYPE_UINT24:
      l_1=mi_uint3korr(a);
      l_2=mi_uint3korr(b);
unknown's avatar
unknown committed
304
      if (piks && (flag = CMP_NUM(l_1,l_2)))
305 306 307 308 309 310 311
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 3;
      break;
    case HA_KEYTYPE_FLOAT:
      mi_float4get(f_1,a);
      mi_float4get(f_2,b);
312 313 314 315 316
      /*
        The following may give a compiler warning about floating point
        comparison not being safe, but this is ok in this context as
        we are bascily doing sorting
      */
unknown's avatar
unknown committed
317
      if (piks && (flag = CMP_NUM(f_1,f_2)))
318 319 320 321 322 323 324
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 4; /* sizeof(float); */
      break;
    case HA_KEYTYPE_DOUBLE:
      mi_float8get(d_1,a);
      mi_float8get(d_2,b);
325 326 327 328 329
      /*
        The following may give a compiler warning about floating point
        comparison not being safe, but this is ok in this context as
        we are bascily doing sorting
      */
unknown's avatar
unknown committed
330
      if (piks && (flag = CMP_NUM(d_1,d_2)))
331 332 333 334 335 336 337 338
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 8;  /* sizeof(double); */
      break;
    case HA_KEYTYPE_NUM:                                /* Numeric key */
    {
      int swap_flag= 0;
      int alength,blength;
unknown's avatar
unknown committed
339

340 341
      if (keyseg->flag & HA_REVERSE_SORT)
      {
342
        swap_variables(uchar*, a, b);
343 344 345 346 347 348 349
        swap_flag=1;                            /* Remember swap of a & b */
        end= a+ (int) (end-b);
      }
      if (keyseg->flag & HA_SPACE_PACK)
      {
        alength= *a++; blength= *b++;
        end=a+alength;
unknown's avatar
unknown committed
350
        next_key_length=key_length-blength-1;
351 352 353 354 355 356 357 358 359
      }
      else
      {
        alength= (int) (end-a);
        blength=keyseg->length;
        /* remove pre space from keys */
        for ( ; alength && *a == ' ' ; a++, alength--) ;
        for ( ; blength && *b == ' ' ; b++, blength--) ;
      }
unknown's avatar
unknown committed
360
      if (piks)
361
      {
unknown's avatar
unknown committed
362 363 364 365 366
	if (*a == '-')
	{
	  if (*b != '-')
	    return -1;
	  a++; b++;
367 368
	  swap_variables(uchar*, a, b);
	  swap_variables(int, alength, blength);
unknown's avatar
unknown committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
	  swap_flag=1-swap_flag;
	  alength--; blength--;
	  end=a+alength;
	}
	else if (*b == '-')
	  return 1;
	while (alength && (*a == '+' || *a == '0'))
	{
	  a++; alength--;
	}
	while (blength && (*b == '+' || *b == '0'))
	{
	  b++; blength--;
	}
	if (alength != blength)
	  return (alength < blength) ? -1 : 1;
	while (a < end)
	  if (*a++ !=  *b++)
	    return ((int) a[-1] - (int) b[-1]);
388
      }
unknown's avatar
unknown committed
389
      else
390
      {
unknown's avatar
unknown committed
391 392
        b+=(end-a);
        a=end;
393 394 395
      }

      if (swap_flag)                            /* Restore pointers */
396
        swap_variables(uchar*, a, b);
397 398 399 400 401 402 403 404
      break;
    }
#ifdef HAVE_LONG_LONG
    case HA_KEYTYPE_LONGLONG:
    {
      longlong ll_a,ll_b;
      ll_a= mi_sint8korr(a);
      ll_b= mi_sint8korr(b);
unknown's avatar
unknown committed
405
      if (piks && (flag = CMP_NUM(ll_a,ll_b)))
406 407 408 409 410 411 412 413 414 415
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 8;
      break;
    }
    case HA_KEYTYPE_ULONGLONG:
    {
      ulonglong ll_a,ll_b;
      ll_a= mi_uint8korr(a);
      ll_b= mi_uint8korr(b);
unknown's avatar
unknown committed
416
      if (piks && (flag = CMP_NUM(ll_a,ll_b)))
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
        return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
      a=  end;
      b+= 8;
      break;
    }
#endif
    case HA_KEYTYPE_END:                        /* Ready */
      goto end;                                 /* diff_pos is incremented */
    }
  }
  (*diff_pos)++;
end:
  if (!(nextflag & SEARCH_FIND))
  {
    uint i;
    if (nextflag & (SEARCH_NO_FIND | SEARCH_LAST)) /* Find record after key */
      return (nextflag & (SEARCH_BIGGER | SEARCH_LAST)) ? -1 : 1;
    flag=0;
    for (i=keyseg->length ; i-- > 0 ; )
    {
      if (*a++ != *b++)
      {
        flag= FCMP(a[-1],b[-1]);
        break;
      }
    }
    if (nextflag & SEARCH_SAME)
      return (flag);                            /* read same */
    if (nextflag & SEARCH_BIGGER)
      return (flag <= 0 ? -1 : 1);              /* read next */
    return (flag < 0 ? -1 : 1);                 /* read previous */
  }
  return 0;
450
} /* ha_key_cmp */