mi_key.c 14.9 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   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; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   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.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20
   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* Functions to handle keys */

#include "myisamdef.h"
#include "m_ctype.h"
21
#include "sp_defs.h"
unknown's avatar
unknown committed
22 23 24
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
unknown's avatar
unknown committed
25

unknown's avatar
unknown committed
26
#define CHECK_KEYS                              /* Enable safety checks */
unknown's avatar
unknown committed
27

unknown's avatar
unknown committed
28
#define FIX_LENGTH(cs, pos, length, char_length)                            \
29 30 31 32 33
            do {                                                            \
              if (length > char_length)                                     \
                char_length= my_charpos(cs, pos, pos+length, char_length);  \
              set_if_smaller(char_length,length);                           \
            } while(0)
34

unknown's avatar
unknown committed
35 36
static int _mi_put_key_in_record(MI_INFO *info,uint keynr,byte *record);

37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*
  Make a intern key from a record

  SYNOPSIS
    _mi_make_key()
    info		MyiSAM handler
    keynr		key number
    key			Store created key here
    record		Record
    filepos		Position to record in the data file

  RETURN
    Length of key
*/
unknown's avatar
unknown committed
51 52 53 54 55 56

uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key,
		  const byte *record, my_off_t filepos)
{
  byte *pos,*end;
  uchar *start;
unknown's avatar
unknown committed
57
  reg1 HA_KEYSEG *keyseg;
58
  my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
unknown's avatar
unknown committed
59 60
  DBUG_ENTER("_mi_make_key");

unknown's avatar
unknown committed
61
  if (info->s->keyinfo[keynr].flag & HA_SPATIAL)
62
  {
63
    /*
64 65
      TODO: nulls processing
    */
66
#ifdef HAVE_SPATIAL
67
    return sp_make_key(info,keynr,key,record,filepos);
68 69
#else
    DBUG_ASSERT(0); /* mi_open should check that this never happens*/
70
#endif
71 72
  }

unknown's avatar
unknown committed
73 74 75 76 77
  start=key;
  for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
  {
    enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
    uint length=keyseg->length;
78
    uint char_length;
79
    CHARSET_INFO *cs=keyseg->charset;
unknown's avatar
unknown committed
80 81 82 83 84 85 86 87 88 89 90

    if (keyseg->null_bit)
    {
      if (record[keyseg->null_pos] & keyseg->null_bit)
      {
	*key++= 0;				/* NULL in key */
	continue;
      }
      *key++=1;					/* Not NULL */
    }

unknown's avatar
unknown committed
91 92
    char_length= ((!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen :
                  length);
93

unknown's avatar
unknown committed
94
    pos= (byte*) record+keyseg->start;
unknown's avatar
unknown committed
95 96 97 98 99 100 101 102 103 104 105 106 107
    if (type == HA_KEYTYPE_BIT)
    {
      if (keyseg->bit_length)
      {
        uchar bits= get_rec_bits((uchar*) record + keyseg->bit_pos,
                                 keyseg->bit_start, keyseg->bit_length);
        *key++= bits;
        length--;
      }
      memcpy((byte*) key, pos, length);
      key+= length;
      continue;
    }
unknown's avatar
unknown committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121
    if (keyseg->flag & HA_SPACE_PACK)
    {
      end=pos+length;
      if (type != HA_KEYTYPE_NUM)
      {
	while (end > pos && end[-1] == ' ')
	  end--;
      }
      else
      {
	while (pos < end && pos[0] == ' ')
	  pos++;
      }
      length=(uint) (end-pos);
unknown's avatar
unknown committed
122
      FIX_LENGTH(cs, pos, length, char_length);
123 124 125
      store_key_length_inc(key,char_length);
      memcpy((byte*) key,(byte*) pos,(size_t) char_length);
      key+=char_length;
unknown's avatar
unknown committed
126 127
      continue;
    }
128
    if (keyseg->flag & HA_VAR_LENGTH_PART)
unknown's avatar
unknown committed
129
    {
130 131 132 133
      uint pack_length= keyseg->bit_start;
      uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
                        uint2korr(pos));
      pos+= pack_length;			/* Skip VARCHAR length */
unknown's avatar
unknown committed
134
      set_if_smaller(length,tmp_length);
unknown's avatar
unknown committed
135
      FIX_LENGTH(cs, pos, length, char_length);
136 137 138
      store_key_length_inc(key,char_length);
      memcpy((byte*) key,(byte*) pos,(size_t) char_length);
      key+= char_length;
139
      continue;
unknown's avatar
unknown committed
140 141 142 143 144 145
    }
    else if (keyseg->flag & HA_BLOB_PART)
    {
      uint tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
      memcpy_fixed((byte*) &pos,pos+keyseg->bit_start,sizeof(char*));
      set_if_smaller(length,tmp_length);
unknown's avatar
unknown committed
146
      FIX_LENGTH(cs, pos, length, char_length);
147 148 149
      store_key_length_inc(key,char_length);
      memcpy((byte*) key,(byte*) pos,(size_t) char_length);
      key+= char_length;
150
      continue;
unknown's avatar
unknown committed
151 152 153
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {						/* Numerical column */
unknown's avatar
unknown committed
154
#ifdef HAVE_ISNAN
unknown's avatar
unknown committed
155 156
      if (type == HA_KEYTYPE_FLOAT)
      {
unknown's avatar
unknown committed
157 158 159
	float nr;
	float4get(nr,pos);
	if (isnan(nr))
unknown's avatar
unknown committed
160
	{
unknown's avatar
unknown committed
161
	  /* Replace NAN with zero */
162
	  bzero(key,length);
unknown's avatar
unknown committed
163 164
	  key+=length;
	  continue;
unknown's avatar
unknown committed
165 166 167 168
	}
      }
      else if (type == HA_KEYTYPE_DOUBLE)
      {
unknown's avatar
unknown committed
169 170 171
	double nr;
	float8get(nr,pos);
	if (isnan(nr))
unknown's avatar
unknown committed
172
	{
173
	  bzero(key,length);
unknown's avatar
unknown committed
174 175
	  key+=length;
	  continue;
unknown's avatar
unknown committed
176 177 178 179 180 181 182 183 184 185
	}
      }
#endif
      pos+=length;
      while (length--)
      {
	*key++ = *--pos;
      }
      continue;
    }
unknown's avatar
unknown committed
186
    FIX_LENGTH(cs, pos, length, char_length);
187 188
    memcpy((byte*) key, pos, char_length);
    if (length > char_length)
189
      cs->cset->fill(cs, key+char_length, length-char_length, ' ');
unknown's avatar
unknown committed
190 191 192 193 194 195 196 197 198 199 200 201
    key+= length;
  }
  _mi_dpointer(info,key,filepos);
  DBUG_PRINT("exit",("keynr: %d",keynr));
  DBUG_DUMP("key",(byte*) start,(uint) (key-start)+keyseg->length);
  DBUG_EXECUTE("key",
	       _mi_print_key(DBUG_FILE,info->s->keyinfo[keynr].seg,start,
			     (uint) (key-start)););
  DBUG_RETURN((uint) (key-start));		/* Return keylength */
} /* _mi_make_key */


202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/*
  Pack a key to intern format from given format (c_rkey)

  SYNOPSIS
    _mi_pack_key()
    info		MyISAM handler
    uint keynr		key number
    key			Store packed key here
    old			Not packed key
    k_length		Length of 'old' to use
    last_used_keyseg	out parameter.  May be NULL

   RETURN
     length of packed key

217
     last_use_keyseg    Store pointer to the keyseg after the last used one
218
*/
unknown's avatar
unknown committed
219 220

uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
unknown's avatar
unknown committed
221
		  uint k_length, HA_KEYSEG **last_used_keyseg)
unknown's avatar
unknown committed
222
{
223 224
  uchar *start_key=key;
  HA_KEYSEG *keyseg;
225
  my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
unknown's avatar
unknown committed
226 227 228 229 230 231
  DBUG_ENTER("_mi_pack_key");

  for (keyseg=info->s->keyinfo[keynr].seg ;
       keyseg->type && (int) k_length > 0;
       old+=keyseg->length, keyseg++)
  {
232 233 234 235 236 237
    enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
    uint length=min((uint) keyseg->length,(uint) k_length);
    uint char_length;
    uchar *pos;
    CHARSET_INFO *cs=keyseg->charset;

unknown's avatar
unknown committed
238 239 240 241 242 243
    if (keyseg->null_bit)
    {
      k_length--;
      if (!(*key++= (char) 1-*old++))			/* Copy null marker */
      {
	k_length-=length;
244
        if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
245
          k_length-=2;                                  /* Skip length */
unknown's avatar
unknown committed
246 247 248
	continue;					/* Found NULL */
      }
    }
249
    char_length= (!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length;
unknown's avatar
unknown committed
250 251 252
    pos=old;
    if (keyseg->flag & HA_SPACE_PACK)
    {
253
      uchar *end=pos+length;
unknown's avatar
unknown committed
254 255 256 257 258 259 260 261 262 263 264 265
      if (type != HA_KEYTYPE_NUM)
      {
	while (end > pos && end[-1] == ' ')
	  end--;
      }
      else
      {
	while (pos < end && pos[0] == ' ')
	  pos++;
      }
      k_length-=length;
      length=(uint) (end-pos);
unknown's avatar
unknown committed
266
      FIX_LENGTH(cs, pos, length, char_length);
267 268 269
      store_key_length_inc(key,char_length);
      memcpy((byte*) key,pos,(size_t) char_length);
      key+= char_length;
unknown's avatar
unknown committed
270 271
      continue;
    }
272
    else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
unknown's avatar
unknown committed
273
    {
unknown's avatar
unknown committed
274 275 276
      /* Length of key-part used with mi_rkey() always 2 */
      uint tmp_length=uint2korr(pos);
      k_length-= 2+length;
277
      pos+=2;
278
      set_if_smaller(length,tmp_length);	/* Safety */
unknown's avatar
unknown committed
279
      FIX_LENGTH(cs, pos, length, char_length);
280
      store_key_length_inc(key,char_length);
281
      old+=2;					/* Skip length */
282 283
      memcpy((byte*) key, pos,(size_t) char_length);
      key+= char_length;
unknown's avatar
unknown committed
284
      continue;
unknown's avatar
unknown committed
285 286 287 288 289 290 291 292 293 294 295
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {						/* Numerical column */
      pos+=length;
      k_length-=length;
      while (length--)
      {
	*key++ = *--pos;
      }
      continue;
    }
unknown's avatar
unknown committed
296
    FIX_LENGTH(cs, pos, length, char_length);
297 298
    memcpy((byte*) key, pos, char_length);
    if (length > char_length)
299
      cs->cset->fill(cs,key+char_length, length-char_length, ' ');
unknown's avatar
unknown committed
300 301 302
    key+= length;
    k_length-=length;
  }
303 304
  if (last_used_keyseg)
    *last_used_keyseg= keyseg;
unknown's avatar
unknown committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

#ifdef NOT_USED
  if (keyseg->type)
  {
    /* Part-key ; fill with ASCII 0 for easier searching */
    length= (uint) -k_length;			/* unused part of last key */
    do
    {
      if (keyseg->flag & HA_NULL_PART)
	length++;
      if (keyseg->flag & HA_SPACE_PACK)
	length+=2;
      else
	length+= keyseg->length;
      keyseg++;
    } while (keyseg->type);
    bzero((byte*) key,length);
    key+=length;
  }
#endif
  DBUG_RETURN((uint) (key-start_key));
} /* _mi_pack_key */


	/* Put a key in record */
	/* Used when only-keyread is wanted */

static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
				 byte *record)
{
  reg2 byte *key;
  byte *pos,*key_end;
unknown's avatar
unknown committed
337
  reg1 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
338 339 340
  byte *blob_ptr;
  DBUG_ENTER("_mi_put_key_in_record");

341
  if (info->s->base.blobs && info->s->keyinfo[keynr].flag & HA_VAR_LENGTH_KEY)
unknown's avatar
unknown committed
342 343
  {
    if (!(blob_ptr=
344
	  mi_alloc_rec_buff(info, info->s->keyinfo[keynr].keylength,
345
			    &info->rec_buff)))
unknown's avatar
unknown committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
      goto err;
  }
  key=(byte*) info->lastkey;
  key_end=key+info->lastkey_length;
  for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
  {
    if (keyseg->null_bit)
    {
      if (!*key++)
      {
	record[keyseg->null_pos]|= keyseg->null_bit;
	continue;
      }
      record[keyseg->null_pos]&= ~keyseg->null_bit;
    }
unknown's avatar
unknown committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    if (keyseg->type == HA_KEYTYPE_BIT)
    {
      uint length= keyseg->length;

      if (keyseg->bit_length)
      {
        uchar bits= *key++;
        set_rec_bits(bits, record + keyseg->bit_pos, keyseg->bit_start,
                     keyseg->bit_length);
        length--;
      }
      else
      {
        clr_rec_bits(record + keyseg->bit_pos, keyseg->bit_start,
                     keyseg->bit_length);
      }
      memcpy(record + keyseg->start, (byte*) key, length);
      key+= length;
      continue;
    }
unknown's avatar
unknown committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
    if (keyseg->flag & HA_SPACE_PACK)
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
      if (length > keyseg->length || key+length > key_end)
	goto err;
#endif
      pos= record+keyseg->start;
      if (keyseg->type != (int) HA_KEYTYPE_NUM)
      {
	memcpy(pos,key,(size_t) length);
	bfill(pos+length,keyseg->length-length,' ');
      }
      else
      {
	bfill(pos,keyseg->length-length,' ');
	memcpy(pos+keyseg->length-length,key,(size_t) length);
      }
      key+=length;
      continue;
    }

404
    if (keyseg->flag & HA_VAR_LENGTH_PART)
unknown's avatar
unknown committed
405 406 407 408
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
409
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
410 411
	goto err;
#endif
412
      /* Store key length */
413 414 415 416
      if (keyseg->bit_start == 1)
        *(uchar*) (record+keyseg->start)= (uchar) length;
      else
        int2store(record+keyseg->start, length);
417
      /* And key data */
418
      memcpy(record+keyseg->start + keyseg->bit_start, (byte*) key, length);
unknown's avatar
unknown committed
419 420 421 422 423 424 425
      key+= length;
    }
    else if (keyseg->flag & HA_BLOB_PART)
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
426
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
	goto err;
#endif
      memcpy(record+keyseg->start+keyseg->bit_start,
	     (char*) &blob_ptr,sizeof(char*));
      memcpy(blob_ptr,key,length);
      blob_ptr+=length;
      _my_store_blob_length(record+keyseg->start,
			    (uint) keyseg->bit_start,length);
      key+=length;
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {
      byte *to=  record+keyseg->start+keyseg->length;
      byte *end= key+keyseg->length;
#ifdef CHECK_KEYS
      if (end > key_end)
	goto err;
#endif
      do
      {
	 *--to= *key++;
      } while (key != end);
      continue;
    }
    else
    {
#ifdef CHECK_KEYS
      if (key+keyseg->length > key_end)
	goto err;
#endif
      memcpy(record+keyseg->start,(byte*) key,
	     (size_t) keyseg->length);
      key+= keyseg->length;
    }
  }
  DBUG_RETURN(0);

err:
  DBUG_RETURN(1);				/* Crashed row */
} /* _mi_put_key_in_record */


	/* Here when key reads are used */

int _mi_read_key_record(MI_INFO *info, my_off_t filepos, byte *buf)
{
unknown's avatar
unknown committed
473
  fast_mi_writeinfo(info);
unknown's avatar
unknown committed
474 475 476 477 478 479
  if (filepos != HA_OFFSET_ERROR)
  {
    if (info->lastinx >= 0)
    {				/* Read only key */
      if (_mi_put_key_in_record(info,(uint) info->lastinx,buf))
      {
unknown's avatar
unknown committed
480
        mi_print_error(info->s, HA_ERR_CRASHED);
unknown's avatar
unknown committed
481 482 483 484 485 486 487 488 489 490 491
	my_errno=HA_ERR_CRASHED;
	return -1;
      }
      info->update|= HA_STATE_AKTIV; /* We should find a record */
      return 0;
    }
    my_errno=HA_ERR_WRONG_INDEX;
  }
  return(-1);				/* Wrong data to read */
}

492 493 494
  
/*
  Update auto_increment info
unknown's avatar
unknown committed
495

496 497 498 499 500 501 502 503 504 505
  SYNOPSIS
    update_auto_increment()
    info			MyISAM handler
    record			Row to update

  IMPLEMENTATION
    Only replace the auto_increment value if it is higher than the previous
    one. For signed columns we don't update the auto increment value if it's
    less than zero.
*/
unknown's avatar
unknown committed
506 507 508

void update_auto_increment(MI_INFO *info,const byte *record)
{
509 510
  ulonglong value= 0;			/* Store unsigned values here */
  longlong s_value= 0;			/* Store signed values here */
unknown's avatar
unknown committed
511
  HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
512
  const uchar *key= (uchar*) record + keyseg->start;
unknown's avatar
unknown committed
513 514 515

  switch (keyseg->type) {
  case HA_KEYTYPE_INT8:
516 517
    s_value= (longlong) *(char*)key;
    break;
unknown's avatar
unknown committed
518 519 520 521
  case HA_KEYTYPE_BINARY:
    value=(ulonglong)  *(uchar*) key;
    break;
  case HA_KEYTYPE_SHORT_INT:
522 523
    s_value= (longlong) sint2korr(key);
    break;
unknown's avatar
unknown committed
524 525 526 527
  case HA_KEYTYPE_USHORT_INT:
    value=(ulonglong) uint2korr(key);
    break;
  case HA_KEYTYPE_LONG_INT:
528 529
    s_value= (longlong) sint4korr(key);
    break;
unknown's avatar
unknown committed
530 531 532 533
  case HA_KEYTYPE_ULONG_INT:
    value=(ulonglong) uint4korr(key);
    break;
  case HA_KEYTYPE_INT24:
534 535
    s_value= (longlong) sint3korr(key);
    break;
unknown's avatar
unknown committed
536 537 538
  case HA_KEYTYPE_UINT24:
    value=(ulonglong) uint3korr(key);
    break;
539
  case HA_KEYTYPE_FLOAT:                        /* This shouldn't be used */
unknown's avatar
unknown committed
540 541 542
  {
    float f_1;
    float4get(f_1,key);
543 544
    /* Ignore negative values */
    value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
545 546
    break;
  }
547
  case HA_KEYTYPE_DOUBLE:                       /* This shouldn't be used */
unknown's avatar
unknown committed
548 549 550
  {
    double f_1;
    float8get(f_1,key);
551 552
    /* Ignore negative values */
    value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
553 554 555
    break;
  }
  case HA_KEYTYPE_LONGLONG:
556 557
    s_value= sint8korr(key);
    break;
unknown's avatar
unknown committed
558 559 560 561
  case HA_KEYTYPE_ULONGLONG:
    value= uint8korr(key);
    break;
  default:
562 563
    DBUG_ASSERT(0);
    value=0;                                    /* Error */
unknown's avatar
unknown committed
564 565
    break;
  }
566 567 568 569 570 571 572 573

  /*
    The following code works becasue if s_value < 0 then value is 0
    and if s_value == 0 then value will contain either s_value or the
    correct value.
  */
  set_if_bigger(info->s->state.auto_increment,
                (s_value > 0) ? (ulonglong) s_value : value);
unknown's avatar
unknown committed
574
}