key.cc 10.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 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
   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 and fields in forms */

#include "mysql_priv.h"

	/*
	** Search after with key field is. If no key starts with field test
	** if field is part of some key.
	**
	** returns number of key. keylength is set to length of key before
	** (not including) field
	** Used when calculating key for NEXT_NUMBER
	*/

int find_ref_key(TABLE *table,Field *field, uint *key_length)
{
  reg2 int i;
  reg3 KEY *key_info;
  uint fieldpos;

  fieldpos=    field->offset();

	/* Test if some key starts as fieldpos */

  for (i=0, key_info=table->key_info ; i < (int) table->keys ; i++, key_info++)
  {
    if (key_info->key_part[0].offset == fieldpos)
    {						/* Found key. Calc keylength */
      *key_length=0;
      return(i);			/* Use this key */
    }
  }
	/* Test if some key contains fieldpos */

  for (i=0, key_info=table->key_info ; i < (int) table->keys ; i++, key_info++)
  {
    uint j;
    KEY_PART_INFO *key_part;
    *key_length=0;
    for (j=0, key_part=key_info->key_part ;
	 j < key_info->key_parts ;
	 j++, key_part++)
    {
      if (key_part->offset == fieldpos)
	return(i);			/* Use this key */
unknown's avatar
unknown committed
62
      *key_length+=key_part->store_length;
unknown's avatar
unknown committed
63 64 65 66 67 68
    }
  }
  return(-1);					/* No key is ok */
}


69 70
/*
  Copy part of a record that forms a key or key prefix to a buffer.
unknown's avatar
unknown committed
71

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
  SYNOPSIS
    key_copy()
    to_key      buffer that will be used as a key
    from_record full record to be copied from
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be copied

  DESCRIPTION
    The function takes a complete table record (as e.g. retrieved by
    handler::index_read()), and a description of an index on the same table,
    and extracts the first key_length bytes of the record which are part of a
    key into to_key. If length == 0 then copy all bytes from the record that
    form a key.

  RETURN
    None
*/
unknown's avatar
unknown committed
89

90
void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
unknown's avatar
unknown committed
91 92 93 94 95
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
96 97
    key_length= key_info->key_length;
  for (key_part= key_info->key_part; (int) key_length > 0; key_part++)
unknown's avatar
unknown committed
98 99 100
  {
    if (key_part->null_bit)
    {
101
      *to_key++= test(from_record[key_part->null_offset] &
unknown's avatar
unknown committed
102 103 104 105 106 107
		   key_part->null_bit);
      key_length--;
    }
    if (key_part->key_part_flag & HA_BLOB_PART)
    {
      char *pos;
108
      ulong blob_length= ((Field_blob*) key_part->field)->get_length();
109
      key_length-= HA_KEY_BLOB_LENGTH;
unknown's avatar
unknown committed
110
      ((Field_blob*) key_part->field)->get_ptr(&pos);
111 112 113
      length=min(key_length, key_part->length);
      set_if_smaller(blob_length, length);
      int2store(to_key, (uint) blob_length);
114
      to_key+= HA_KEY_BLOB_LENGTH;			// Skip length info
115
      memcpy(to_key, pos, blob_length);
unknown's avatar
unknown committed
116
    }
117 118 119 120 121 122 123
    else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->get_key_image(to_key, length, Field::itRAW);
      to_key+= HA_KEY_BLOB_LENGTH;
    }
unknown's avatar
unknown committed
124 125
    else
    {
126 127
      length= min(key_length, key_part->length);
      memcpy(to_key, from_record + key_part->offset, (size_t) length);
unknown's avatar
unknown committed
128
    }
129 130
    to_key+= length;
    key_length-= length;
unknown's avatar
unknown committed
131
  }
132
}
unknown's avatar
unknown committed
133 134


135 136
/*
  Restore a key from some buffer to record.
unknown's avatar
unknown committed
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
  SYNOPSIS
    key_restore()
    to_record   record buffer where the key will be restored to
    from_key    buffer that contains a key
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be restored

  DESCRIPTION
    This function converts a key into record format. It can be used in cases
    when we want to return a key as a result row.

  RETURN
    None
*/

void key_restore(byte *to_record, byte *from_key, KEY *key_info,
                 uint key_length)
unknown's avatar
unknown committed
155 156 157 158 159 160
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
  {
161
    key_length= key_info->key_length;
unknown's avatar
unknown committed
162
  }
163
  for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
unknown's avatar
unknown committed
164 165 166
  {
    if (key_part->null_bit)
    {
167 168
      if (*from_key++)
	to_record[key_part->null_offset]|= key_part->null_bit;
unknown's avatar
unknown committed
169
      else
170
	to_record[key_part->null_offset]&= ~key_part->null_bit;
unknown's avatar
unknown committed
171 172 173 174
      key_length--;
    }
    if (key_part->key_part_flag & HA_BLOB_PART)
    {
175
      uint blob_length= uint2korr(from_key);
176 177
      from_key+= HA_KEY_BLOB_LENGTH;
      key_length-= HA_KEY_BLOB_LENGTH;
unknown's avatar
unknown committed
178
      ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
179 180
					       (char*) from_key);
      length= key_part->length;
unknown's avatar
unknown committed
181
    }
182 183 184 185 186 187 188
    else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->set_key_image(from_key, length);
      from_key+= HA_KEY_BLOB_LENGTH;
    }
unknown's avatar
unknown committed
189 190
    else
    {
191 192
      length= min(key_length, key_part->length);
      memcpy(to_record + key_part->offset, from_key, (size_t) length);
unknown's avatar
unknown committed
193
    }
194 195
    from_key+= length;
    key_length-= length;
unknown's avatar
unknown committed
196
  }
197
}
unknown's avatar
unknown committed
198 199


unknown's avatar
unknown committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/*
  Compare if a key has changed

  SYNOPSIS
    key_cmp_if_same()
    table		TABLE
    key			key to compare to row
    idx			Index used
    key_length		Length of key

  NOTES
    In theory we could just call field->cmp() for all field types,
    but as we are only interested if a key has changed (not if the key is
    larger or smaller than the previous value) we can do things a bit
    faster by using memcmp() instead.

  RETURN
    0	If key is equal
    1	Key has changed
*/
unknown's avatar
unknown committed
220

unknown's avatar
unknown committed
221
bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
unknown's avatar
unknown committed
222 223 224 225 226 227 228 229 230 231 232 233
{
  uint length;
  KEY_PART_INFO *key_part;

  for (key_part=table->key_info[idx].key_part;
       (int) key_length > 0;
       key_part++, key+=length, key_length-=length)
  {
    if (key_part->null_bit)
    {
      key_length--;
      if (*key != test(table->record[0][key_part->null_offset] & 
234
		       key_part->null_bit))
unknown's avatar
unknown committed
235 236 237 238 239 240 241 242
	return 1;
      if (*key)
      {
	length=key_part->store_length;
	continue;
      }
      key++;
    }
243
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
244
    {
245
      if (key_part->field->key_cmp(key, key_part->length))
unknown's avatar
unknown committed
246
	return 1;
247
      length=key_part->length+HA_KEY_BLOB_LENGTH;
unknown's avatar
unknown committed
248 249 250 251 252 253 254
    }
    else
    {
      length=min(key_length,key_part->length);
      if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
				  FIELDFLAG_PACK)))
      {
255 256 257 258 259 260 261 262 263 264
        CHARSET_INFO *cs= key_part->field->charset();
        uint char_length= key_part->length / cs->mbmaxlen;
        const byte *pos= table->record[0] + key_part->offset;
        if (length > char_length)
        {
          char_length= my_charpos(cs, pos, pos + length, char_length);
          set_if_smaller(char_length, length);
        }
	if (cs->coll->strnncollsp(cs,
                                  (const uchar*) key, length,
265
                                  (const uchar*) pos, char_length, 0))
unknown's avatar
unknown committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	  return 1;
      }
      else if (memcmp(key,table->record[0]+key_part->offset,length))
	return 1;
    }
  }
  return 0;
}

	/* unpack key-fields from record to some buffer */
	/* This is used to get a good error message */

void key_unpack(String *to,TABLE *table,uint idx)
{
  KEY_PART_INFO *key_part,*key_part_end;
  Field *field;
  String tmp;
  DBUG_ENTER("key_unpack");

  to->length(0);
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    if (to->length())
      to->append('-');
    if (key_part->null_bit)
    {
      if (table->record[0][key_part->null_offset] & key_part->null_bit)
      {
297
	to->append("NULL", 4);
unknown's avatar
unknown committed
298 299 300 301 302
	continue;
      }
    }
    if ((field=key_part->field))
    {
303
      field->val_str(&tmp);
unknown's avatar
unknown committed
304 305 306 307 308
      if (key_part->length < field->pack_length())
	tmp.length(min(tmp.length(),key_part->length));
      to->append(tmp);
    }
    else
309
      to->append("???", 3);
unknown's avatar
unknown committed
310 311 312 313 314
  }
  DBUG_VOID_RETURN;
}


315 316 317 318
/*
  Return 1 if any field in a list is part of key or the key uses a field
  that is automaticly updated (like a timestamp)
*/
unknown's avatar
unknown committed
319 320 321

bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
{
unknown's avatar
unknown committed
322
  List_iterator_fast<Item> f(fields);
unknown's avatar
unknown committed
323 324 325 326 327 328 329
  KEY_PART_INFO *key_part,*key_part_end;
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    Item_field *field;
unknown's avatar
unknown committed
330

331 332 333
    if (key_part->field == table->timestamp_field)
      return 1;					// Can't be used for update

unknown's avatar
unknown committed
334 335 336 337 338 339 340
    f.rewind();
    while ((field=(Item_field*) f++))
    {
      if (key_part->field == field->field)
	return 1;
    }
  }
341 342 343 344 345 346

  /*
    If table handler has primary key as part of the index, check that primary
    key is not updated
  */
  if (idx != table->primary_key && table->primary_key < MAX_KEY &&
347
      (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
348
    return check_if_key_used(table, table->primary_key, fields);
unknown's avatar
unknown committed
349 350
  return 0;
}
unknown's avatar
unknown committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 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


/*
  Compare key in row to a given key

  SYNOPSIS
    key_cmp()
    key_part		Key part handler
    key			Key to compare to value in table->record[0]
    key_length		length of 'key'

  RETURN
    The return value is SIGN(key_in_row - range_key):

    0			Key is equal to range or 'range' == 0 (no range)
   -1			Key is less than range
    1			Key is larger than range
*/

int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length)
{
  uint store_length;

  for (const byte *end=key + key_length;
       key < end;
       key+= store_length, key_part++)
  {
    int cmp;
    store_length= key_part->store_length;
    if (key_part->null_bit)
    {
      /* This key part allows null values; NULL is lower than everything */
      register bool field_is_null= key_part->field->is_null();
      if (*key)                                 // If range key is null
      {
	/* the range is expecting a null value */
	if (!field_is_null)
	  return 1;                             // Found key is > range
        /* null -- exact match, go to next key part */
	continue;
      }
      else if (field_is_null)
	return -1;                              // NULL is less than any value
      key++;					// Skip null byte
      store_length--;
    }
    if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
      return -1;
    if (cmp > 0)
      return 1;
  }
  return 0;                                     // Keys are equal
}