mi_key.c 15.2 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
unknown's avatar
unknown committed
67
    DBUG_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
    if (keyseg->flag & HA_SPACE_PACK)
    {
110
      end= pos + length;
unknown's avatar
unknown committed
111 112 113 114 115 116 117 118 119 120
      if (type != HA_KEYTYPE_NUM)
      {
	while (end > pos && end[-1] == ' ')
	  end--;
      }
      else
      {
	while (pos < end && pos[0] == ' ')
	  pos++;
      }
121 122
      length=(uint) (end-pos);
      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
      uint pack_length= (keyseg->bit_start == 1 ? 1 : 2);
131 132 133
      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, (char*) 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
        {
246
          k_length-=2;                                  /* Skip length */
247 248
          old+= 2;
        }
unknown's avatar
unknown committed
249 250 251
	continue;					/* Found NULL */
      }
    }
252
    char_length= (!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length;
unknown's avatar
unknown committed
253 254 255
    pos=old;
    if (keyseg->flag & HA_SPACE_PACK)
    {
256
      uchar *end=pos+length;
unknown's avatar
unknown committed
257 258 259 260 261 262 263 264 265 266 267 268
      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
269
      FIX_LENGTH(cs, pos, length, char_length);
270 271 272
      store_key_length_inc(key,char_length);
      memcpy((byte*) key,pos,(size_t) char_length);
      key+= char_length;
unknown's avatar
unknown committed
273 274
      continue;
    }
275
    else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
unknown's avatar
unknown committed
276
    {
unknown's avatar
unknown committed
277 278 279
      /* Length of key-part used with mi_rkey() always 2 */
      uint tmp_length=uint2korr(pos);
      k_length-= 2+length;
280
      pos+=2;
281
      set_if_smaller(length,tmp_length);	/* Safety */
unknown's avatar
unknown committed
282
      FIX_LENGTH(cs, pos, length, char_length);
283
      store_key_length_inc(key,char_length);
284
      old+=2;					/* Skip length */
285 286
      memcpy((byte*) key, pos,(size_t) char_length);
      key+= char_length;
unknown's avatar
unknown committed
287
      continue;
unknown's avatar
unknown committed
288 289 290 291 292 293 294 295 296 297 298
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {						/* Numerical column */
      pos+=length;
      k_length-=length;
      while (length--)
      {
	*key++ = *--pos;
      }
      continue;
    }
unknown's avatar
unknown committed
299
    FIX_LENGTH(cs, pos, length, char_length);
300 301
    memcpy((byte*) key, pos, char_length);
    if (length > char_length)
302
      cs->cset->fill(cs, (char*) key+char_length, length-char_length, ' ');
unknown's avatar
unknown committed
303 304 305
    key+= length;
    k_length-=length;
  }
306 307
  if (last_used_keyseg)
    *last_used_keyseg= keyseg;
unknown's avatar
unknown committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

#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 */


332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

/*
  Store found key in record

  SYNOPSIS
    _mi_put_key_in_record()
    info		MyISAM handler
    keynr		Key number that was used
    record 		Store key here

    Last read key is in info->lastkey

 NOTES
   Used when only-keyread is wanted

 RETURN
   0   ok
   1   error
*/
unknown's avatar
unknown committed
351 352 353 354 355 356

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
357
  reg1 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
358 359 360
  byte *blob_ptr;
  DBUG_ENTER("_mi_put_key_in_record");

361
  blob_ptr= (byte*) info->lastkey2;             /* Place to put blob parts */
362
  key=(byte*) info->lastkey;                    /* KEy that was read */
unknown's avatar
unknown committed
363 364 365 366 367 368 369 370 371 372 373 374
  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
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
    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
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    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;
    }

418
    if (keyseg->flag & HA_VAR_LENGTH_PART)
unknown's avatar
unknown committed
419 420 421 422
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
423
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
424 425
	goto err;
#endif
426
      /* Store key length */
427 428 429 430
      if (keyseg->bit_start == 1)
        *(uchar*) (record+keyseg->start)= (uchar) length;
      else
        int2store(record+keyseg->start, length);
431
      /* And key data */
432
      memcpy(record+keyseg->start + keyseg->bit_start, (byte*) key, length);
unknown's avatar
unknown committed
433 434 435 436 437 438 439
      key+= length;
    }
    else if (keyseg->flag & HA_BLOB_PART)
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
440
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
441 442 443 444 445 446
	goto err;
#endif
      memcpy(record+keyseg->start+keyseg->bit_start,
	     (char*) &blob_ptr,sizeof(char*));
      memcpy(blob_ptr,key,length);
      blob_ptr+=length;
447 448 449 450

      /* The above changed info->lastkey2. Inform mi_rnext_same(). */
      info->update&= ~HA_STATE_RNEXT_SAME;

451
      _mi_store_blob_length(record+keyseg->start,
unknown's avatar
unknown committed
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
			    (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
491
  fast_mi_writeinfo(info);
unknown's avatar
unknown committed
492 493 494 495 496 497
  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
498
        mi_print_error(info->s, HA_ERR_CRASHED);
unknown's avatar
unknown committed
499 500 501 502 503 504 505 506 507 508 509
	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 */
}

510 511 512
  
/*
  Update auto_increment info
unknown's avatar
unknown committed
513

514 515 516 517 518 519 520 521 522 523
  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
524 525 526

void update_auto_increment(MI_INFO *info,const byte *record)
{
527 528
  ulonglong value= 0;			/* Store unsigned values here */
  longlong s_value= 0;			/* Store signed values here */
unknown's avatar
unknown committed
529
  HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
530
  const uchar *key= (uchar*) record + keyseg->start;
unknown's avatar
unknown committed
531 532 533

  switch (keyseg->type) {
  case HA_KEYTYPE_INT8:
534 535
    s_value= (longlong) *(char*)key;
    break;
unknown's avatar
unknown committed
536 537 538 539
  case HA_KEYTYPE_BINARY:
    value=(ulonglong)  *(uchar*) key;
    break;
  case HA_KEYTYPE_SHORT_INT:
540 541
    s_value= (longlong) sint2korr(key);
    break;
unknown's avatar
unknown committed
542 543 544 545
  case HA_KEYTYPE_USHORT_INT:
    value=(ulonglong) uint2korr(key);
    break;
  case HA_KEYTYPE_LONG_INT:
546 547
    s_value= (longlong) sint4korr(key);
    break;
unknown's avatar
unknown committed
548 549 550 551
  case HA_KEYTYPE_ULONG_INT:
    value=(ulonglong) uint4korr(key);
    break;
  case HA_KEYTYPE_INT24:
552 553
    s_value= (longlong) sint3korr(key);
    break;
unknown's avatar
unknown committed
554 555 556
  case HA_KEYTYPE_UINT24:
    value=(ulonglong) uint3korr(key);
    break;
557
  case HA_KEYTYPE_FLOAT:                        /* This shouldn't be used */
unknown's avatar
unknown committed
558 559 560
  {
    float f_1;
    float4get(f_1,key);
561 562
    /* Ignore negative values */
    value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
563 564
    break;
  }
565
  case HA_KEYTYPE_DOUBLE:                       /* This shouldn't be used */
unknown's avatar
unknown committed
566 567 568
  {
    double f_1;
    float8get(f_1,key);
569 570
    /* Ignore negative values */
    value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
571 572 573
    break;
  }
  case HA_KEYTYPE_LONGLONG:
574 575
    s_value= sint8korr(key);
    break;
unknown's avatar
unknown committed
576 577 578 579
  case HA_KEYTYPE_ULONGLONG:
    value= uint8korr(key);
    break;
  default:
580 581
    DBUG_ASSERT(0);
    value=0;                                    /* Error */
unknown's avatar
unknown committed
582 583
    break;
  }
584 585 586 587 588 589 590 591

  /*
    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
592
}