mi_key.c 12.8 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 26 27 28 29 30 31 32 33 34

#define CHECK_KEYS

static int _mi_put_key_in_record(MI_INFO *info,uint keynr,byte *record);

	/*
	** Make a intern key from a record
	** Ret: Length of key
	*/

35 36 37
#define my_charpos(cs, b, e, num)\
 (cs)->cset->charpos((cs), (const char*) (b), (const char *)(e), (num))

unknown's avatar
unknown committed
38 39 40 41 42
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
43
  reg1 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
44 45
  DBUG_ENTER("_mi_make_key");

46 47 48 49 50
  if(info->s->keyinfo[keynr].flag & HA_SPATIAL)
  {
    /* 
      TODO: nulls processing
    */
51
#ifdef HAVE_SPATIAL
52
    return sp_make_key(info,keynr,key,record,filepos);
53 54
#else
    DBUG_ASSERT(0); /* mi_open should check that this never happens*/
55
#endif
56 57
  }

unknown's avatar
unknown committed
58 59 60 61 62
  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;
63 64
    uint char_length;
    CHARSET_INFO *cs;
unknown's avatar
unknown committed
65 66 67 68 69 70 71 72 73 74 75

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

76 77 78 79 80 81 82 83 84
    char_length= (cs= keyseg->charset) && (cs->mbmaxlen > 1) ?
                 length / cs->mbmaxlen : 0;
    
    if (info->s->keyinfo[keynr].flag & HA_FULLTEXT)
    {
      /* Ask Serg to make a better fix */
      char_length= 0;
    }
    
unknown's avatar
unknown committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    pos= (byte*) record+keyseg->start;
    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);
100 101 102 103 104
      if (char_length && length > char_length)
      {
        char_length= my_charpos(cs, pos, pos+length, char_length);
        set_if_smaller(length, char_length);
      }
unknown's avatar
unknown committed
105 106 107 108 109 110 111 112 113 114 115
      store_key_length_inc(key,length);
      memcpy((byte*) key,(byte*) pos,(size_t) length);
      key+=length;
      continue;
    }
    if (keyseg->flag & HA_VAR_LENGTH)
    {
      uint tmp_length=uint2korr(pos);
      pos+=2;					/* Skip VARCHAR length */
      set_if_smaller(length,tmp_length);
      store_key_length_inc(key,length);
116 117 118
      memcpy((byte*) key, pos, length);
      key+= length;
      continue;
unknown's avatar
unknown committed
119 120 121 122 123 124
    }
    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);
125 126 127 128 129 130 131
#if NOT_YET_BLOB_PART
      if (char_length && length > char_length)
      {
        char_length= my_charpos(cs, pos, pos+length, char_length);
        set_if_smaller(length, char_length);
      }
#endif
unknown's avatar
unknown committed
132
      store_key_length_inc(key,length);
133 134 135
      memcpy((byte*) key, pos, length);
      key+= length;
      continue;
unknown's avatar
unknown committed
136 137 138
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {						/* Numerical column */
unknown's avatar
unknown committed
139
#ifdef HAVE_ISNAN
unknown's avatar
unknown committed
140 141
      if (type == HA_KEYTYPE_FLOAT)
      {
unknown's avatar
unknown committed
142 143 144
	float nr;
	float4get(nr,pos);
	if (isnan(nr))
unknown's avatar
unknown committed
145
	{
unknown's avatar
unknown committed
146 147 148 149
	  /* Replace NAN with zero */
 	  bzero(key,length);
	  key+=length;
	  continue;
unknown's avatar
unknown committed
150 151 152 153
	}
      }
      else if (type == HA_KEYTYPE_DOUBLE)
      {
unknown's avatar
unknown committed
154 155 156
	double nr;
	float8get(nr,pos);
	if (isnan(nr))
unknown's avatar
unknown committed
157
	{
unknown's avatar
unknown committed
158 159 160
 	  bzero(key,length);
	  key+=length;
	  continue;
unknown's avatar
unknown committed
161 162 163 164 165 166 167 168 169 170
	}
      }
#endif
      pos+=length;
      while (length--)
      {
	*key++ = *--pos;
      }
      continue;
    }
171 172 173 174 175 176 177
#ifdef NOT_YET_FIXED_LENGTH_KEY
    if (char_length && length > char_length)
    {
      char_length= my_charpos(cs, pos, pos+length, char_length);
      set_if_smaller(length, char_length);
    }
#endif
unknown's avatar
unknown committed
178 179 180 181 182 183 184 185 186 187 188 189 190
    memcpy((byte*) key, pos, length);
    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 */


191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
/*
  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

     last_use_keyseg 	Store pointer to the keyseg after the last used one
*/
unknown's avatar
unknown committed
208 209

uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
unknown's avatar
unknown committed
210
		  uint k_length, HA_KEYSEG **last_used_keyseg)
unknown's avatar
unknown committed
211 212 213
{
  uint length;
  uchar *pos,*end,*start_key=key;
unknown's avatar
unknown committed
214
  reg1 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
  enum ha_base_keytype type;
  DBUG_ENTER("_mi_pack_key");

  start_key=key;
  for (keyseg=info->s->keyinfo[keynr].seg ;
       keyseg->type && (int) k_length > 0;
       old+=keyseg->length, keyseg++)
  {
    length=min((uint) keyseg->length,(uint) k_length);
    type=(enum ha_base_keytype) keyseg->type;
    if (keyseg->null_bit)
    {
      k_length--;
      if (!(*key++= (char) 1-*old++))			/* Copy null marker */
      {
	k_length-=length;
	continue;					/* Found NULL */
      }
    }
    pos=old;
    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++;
      }
      k_length-=length;
      length=(uint) (end-pos);
      store_key_length_inc(key,length);
      memcpy((byte*) key,pos,(size_t) length);
      key+= length;
      continue;
    }
    else if (keyseg->flag & (HA_VAR_LENGTH | HA_BLOB_PART))
    {
unknown's avatar
unknown committed
257 258 259
      /* Length of key-part used with mi_rkey() always 2 */
      uint tmp_length=uint2korr(pos);
      k_length-= 2+length;
260
      set_if_smaller(length,tmp_length);	/* Safety */
unknown's avatar
unknown committed
261
      store_key_length_inc(key,length);
262
      old+=2;					/* Skip length */
unknown's avatar
unknown committed
263 264 265
      memcpy((byte*) key, pos+2,(size_t) length);
      key+= length;
      continue;
unknown's avatar
unknown committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    }
    else if (keyseg->flag & HA_SWAP_KEY)
    {						/* Numerical column */
      pos+=length;
      k_length-=length;
      while (length--)
      {
	*key++ = *--pos;
      }
      continue;
    }
    memcpy((byte*) key,pos,(size_t) length);
    key+= length;
    k_length-=length;
  }
281 282
  if (last_used_keyseg)
    *last_used_keyseg= keyseg;
unknown's avatar
unknown committed
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

#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
315
  reg1 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
316 317 318
  byte *blob_ptr;
  DBUG_ENTER("_mi_put_key_in_record");

319
  if (info->s->base.blobs && info->s->keyinfo[keynr].flag & HA_VAR_LENGTH_KEY)
unknown's avatar
unknown committed
320 321
  {
    if (!(blob_ptr=
322
	  mi_alloc_rec_buff(info, info->s->keyinfo[keynr].keylength,
323
			    &info->rec_buff)))
unknown's avatar
unknown committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
      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;
    }
    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;
    }

    if (keyseg->flag & HA_VAR_LENGTH)
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
367
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
368 369 370 371 372 373 374 375 376 377
	goto err;
#endif
      memcpy(record+keyseg->start,(byte*) key, length);
      key+= length;
    }
    else if (keyseg->flag & HA_BLOB_PART)
    {
      uint length;
      get_key_length(length,key);
#ifdef CHECK_KEYS
unknown's avatar
unknown committed
378
      if (length > keyseg->length || key+length > key_end)
unknown's avatar
unknown committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	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
425
  fast_mi_writeinfo(info);
unknown's avatar
unknown committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
  if (filepos != HA_OFFSET_ERROR)
  {
    if (info->lastinx >= 0)
    {				/* Read only key */
      if (_mi_put_key_in_record(info,(uint) info->lastinx,buf))
      {
	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 */
}

443 444 445
  
/*
  Update auto_increment info
unknown's avatar
unknown committed
446

447 448 449 450 451 452 453 454 455 456
  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
457 458 459

void update_auto_increment(MI_INFO *info,const byte *record)
{
460 461
  ulonglong value= 0;			/* Store unsigned values here */
  longlong s_value= 0;			/* Store signed values here */
unknown's avatar
unknown committed
462
  HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
463
  const uchar *key= (uchar*) record + keyseg->start;
unknown's avatar
unknown committed
464 465 466

  switch (keyseg->type) {
  case HA_KEYTYPE_INT8:
467 468
    s_value= (longlong) *(char*)key;
    break;
unknown's avatar
unknown committed
469 470 471 472
  case HA_KEYTYPE_BINARY:
    value=(ulonglong)  *(uchar*) key;
    break;
  case HA_KEYTYPE_SHORT_INT:
473 474
    s_value= (longlong) sint2korr(key);
    break;
unknown's avatar
unknown committed
475 476 477 478
  case HA_KEYTYPE_USHORT_INT:
    value=(ulonglong) uint2korr(key);
    break;
  case HA_KEYTYPE_LONG_INT:
479 480
    s_value= (longlong) sint4korr(key);
    break;
unknown's avatar
unknown committed
481 482 483 484
  case HA_KEYTYPE_ULONG_INT:
    value=(ulonglong) uint4korr(key);
    break;
  case HA_KEYTYPE_INT24:
485 486
    s_value= (longlong) sint3korr(key);
    break;
unknown's avatar
unknown committed
487 488 489
  case HA_KEYTYPE_UINT24:
    value=(ulonglong) uint3korr(key);
    break;
490
  case HA_KEYTYPE_FLOAT:                        /* This shouldn't be used */
unknown's avatar
unknown committed
491 492 493
  {
    float f_1;
    float4get(f_1,key);
494 495
    /* Ignore negative values */
    value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
496 497
    break;
  }
498
  case HA_KEYTYPE_DOUBLE:                       /* This shouldn't be used */
unknown's avatar
unknown committed
499 500 501
  {
    double f_1;
    float8get(f_1,key);
502 503
    /* Ignore negative values */
    value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
unknown's avatar
unknown committed
504 505 506
    break;
  }
  case HA_KEYTYPE_LONGLONG:
507 508
    s_value= sint8korr(key);
    break;
unknown's avatar
unknown committed
509 510 511 512
  case HA_KEYTYPE_ULONGLONG:
    value= uint8korr(key);
    break;
  default:
513 514
    DBUG_ASSERT(0);
    value=0;                                    /* Error */
unknown's avatar
unknown committed
515 516
    break;
  }
517 518 519 520 521 522 523 524

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