~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/key.cc

  • Committer: Monty Taylor
  • Date: 2008-08-04 22:01:39 UTC
  • mto: (261.1.4 drizzle)
  • mto: This revision was merged to the branch mainline in revision 262.
  • Revision ID: monty@inaugust.com-20080804220139-fy862jc9lykayvka
Moved libdrizzle.ver.in to libdrizzle.ver.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2000-2006 MySQL AB
2
 
 
3
 
   This program is free software; you can redistribute it and/or modify
4
 
   it under the terms of the GNU General Public License as published by
5
 
   the Free Software Foundation; version 2 of the License.
6
 
 
7
 
   This program is distributed in the hope that it will be useful,
8
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 
   GNU General Public License for more details.
11
 
 
12
 
   You should have received a copy of the GNU General Public License
13
 
   along with this program; if not, write to the Free Software
14
 
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
 
 
16
 
 
17
 
/* Functions to handle keys and fields in forms */
18
 
 
19
 
#include <drizzled/server_includes.h>
20
 
 
21
 
/*
22
 
  Search after a key that starts with 'field'
23
 
 
24
 
  SYNOPSIS
25
 
    find_ref_key()
26
 
    key                 First key to check
27
 
    key_count           How many keys to check
28
 
    record              Start of record
29
 
    field               Field to search after
30
 
    key_length          On partial match, contains length of fields before
31
 
                        field
32
 
    keypart             key part # of a field
33
 
 
34
 
  NOTES
35
 
   Used when calculating key for NEXT_NUMBER
36
 
 
37
 
  IMPLEMENTATION
38
 
    If no key starts with field test if field is part of some key. If we find
39
 
    one, then return first key and set key_length to the number of bytes
40
 
    preceding 'field'.
41
 
 
42
 
  RETURN
43
 
   -1  field is not part of the key
44
 
   #   Key part for key matching key.
45
 
       key_length is set to length of key before (not including) field
46
 
*/
47
 
 
48
 
int find_ref_key(KEY *key, uint32_t key_count, unsigned char *record, Field *field,
49
 
                 uint32_t *key_length, uint32_t *keypart)
50
 
{
51
 
  register int i;
52
 
  register KEY *key_info;
53
 
  uint32_t fieldpos;
54
 
 
55
 
  fieldpos= field->offset(record);
56
 
 
57
 
  /* Test if some key starts as fieldpos */
58
 
  for (i= 0, key_info= key ;
59
 
       i < (int) key_count ;
60
 
       i++, key_info++)
61
 
  {
62
 
    if (key_info->key_part[0].offset == fieldpos)
63
 
    {                                           /* Found key. Calc keylength */
64
 
      *key_length= *keypart= 0;
65
 
      return i;                                 /* Use this key */
66
 
    }
67
 
  }
68
 
 
69
 
  /* Test if some key contains fieldpos */
70
 
  for (i= 0, key_info= key;
71
 
       i < (int) key_count ;
72
 
       i++, key_info++)
73
 
  {
74
 
    uint32_t j;
75
 
    KEY_PART_INFO *key_part;
76
 
    *key_length=0;
77
 
    for (j=0, key_part=key_info->key_part ;
78
 
         j < key_info->key_parts ;
79
 
         j++, key_part++)
80
 
    {
81
 
      if (key_part->offset == fieldpos)
82
 
      {
83
 
        *keypart= j;
84
 
        return i;                               /* Use this key */
85
 
      }
86
 
      *key_length+= key_part->store_length;
87
 
    }
88
 
  }
89
 
  return(-1);                                   /* No key is ok */
90
 
}
91
 
 
92
 
 
93
 
/**
94
 
  Copy part of a record that forms a key or key prefix to a buffer.
95
 
 
96
 
    The function takes a complete table record (as e.g. retrieved by
97
 
    handler::index_read()), and a description of an index on the same table,
98
 
    and extracts the first key_length bytes of the record which are part of a
99
 
    key into to_key. If length == 0 then copy all bytes from the record that
100
 
    form a key.
101
 
 
102
 
  @param to_key      buffer that will be used as a key
103
 
  @param from_record full record to be copied from
104
 
  @param key_info    descriptor of the index
105
 
  @param key_length  specifies length of all keyparts that will be copied
106
 
*/
107
 
 
108
 
void key_copy(unsigned char *to_key, unsigned char *from_record, KEY *key_info,
109
 
              unsigned int key_length)
110
 
{
111
 
  uint32_t length;
112
 
  KEY_PART_INFO *key_part;
113
 
 
114
 
  if (key_length == 0)
115
 
    key_length= key_info->key_length;
116
 
  for (key_part= key_info->key_part; (int) key_length > 0; key_part++)
117
 
  {
118
 
    if (key_part->null_bit)
119
 
    {
120
 
      *to_key++= test(from_record[key_part->null_offset] &
121
 
                   key_part->null_bit);
122
 
      key_length--;
123
 
    }
124
 
    if (key_part->key_part_flag & HA_BLOB_PART ||
125
 
        key_part->key_part_flag & HA_VAR_LENGTH_PART)
126
 
    {
127
 
      key_length-= HA_KEY_BLOB_LENGTH;
128
 
      length= cmin((uint16_t)key_length, key_part->length);
129
 
      key_part->field->get_key_image(to_key, length, Field::itRAW);
130
 
      to_key+= HA_KEY_BLOB_LENGTH;
131
 
    }
132
 
    else
133
 
    {
134
 
      length= cmin((uint16_t)key_length, key_part->length);
135
 
      Field *field= key_part->field;
136
 
      const CHARSET_INFO * const cs= field->charset();
137
 
      uint32_t bytes= field->get_key_image(to_key, length, Field::itRAW);
138
 
      if (bytes < length)
139
 
        cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
140
 
    }
141
 
    to_key+= length;
142
 
    key_length-= length;
143
 
  }
144
 
}
145
 
 
146
 
 
147
 
/**
148
 
  Zero the null components of key tuple.
149
 
*/
150
 
 
151
 
void key_zero_nulls(unsigned char *tuple, KEY *key_info)
152
 
{
153
 
  KEY_PART_INFO *key_part= key_info->key_part;
154
 
  KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
155
 
  for (; key_part != key_part_end; key_part++)
156
 
  {
157
 
    if (key_part->null_bit && *tuple)
158
 
      memset(tuple+1, 0, key_part->store_length-1);
159
 
    tuple+= key_part->store_length;
160
 
  }
161
 
}
162
 
 
163
 
 
164
 
/*
165
 
  Restore a key from some buffer to record.
166
 
 
167
 
    This function converts a key into record format. It can be used in cases
168
 
    when we want to return a key as a result row.
169
 
 
170
 
  @param to_record   record buffer where the key will be restored to
171
 
  @param from_key    buffer that contains a key
172
 
  @param key_info    descriptor of the index
173
 
  @param key_length  specifies length of all keyparts that will be restored
174
 
*/
175
 
 
176
 
void key_restore(unsigned char *to_record, unsigned char *from_key, KEY *key_info,
177
 
                 uint16_t key_length)
178
 
{
179
 
  uint32_t length;
180
 
  KEY_PART_INFO *key_part;
181
 
 
182
 
  if (key_length == 0)
183
 
  {
184
 
    key_length= key_info->key_length;
185
 
  }
186
 
  for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
187
 
  {
188
 
    unsigned char used_uneven_bits= 0;
189
 
    if (key_part->null_bit)
190
 
    {
191
 
      if (*from_key++)
192
 
        to_record[key_part->null_offset]|= key_part->null_bit;
193
 
      else
194
 
        to_record[key_part->null_offset]&= ~key_part->null_bit;
195
 
      key_length--;
196
 
    }
197
 
    if (key_part->key_part_flag & HA_BLOB_PART)
198
 
    {
199
 
      /*
200
 
        This in fact never happens, as we have only partial BLOB
201
 
        keys yet anyway, so it's difficult to find any sence to
202
 
        restore the part of a record.
203
 
        Maybe this branch is to be removed, but now we
204
 
        have to ignore GCov compaining.
205
 
      */
206
 
      uint32_t blob_length= uint2korr(from_key);
207
 
      Field_blob *field= (Field_blob*) key_part->field;
208
 
      from_key+= HA_KEY_BLOB_LENGTH;
209
 
      key_length-= HA_KEY_BLOB_LENGTH;
210
 
      field->set_ptr_offset(to_record - field->table->record[0],
211
 
                            (ulong) blob_length, from_key);
212
 
      length= key_part->length;
213
 
    }
214
 
    else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
215
 
    {
216
 
      Field *field= key_part->field;
217
 
      my_ptrdiff_t ptrdiff= to_record - field->table->record[0];
218
 
      field->move_field_offset(ptrdiff);
219
 
      key_length-= HA_KEY_BLOB_LENGTH;
220
 
      length= cmin(key_length, key_part->length);
221
 
      field->set_key_image(from_key, length);
222
 
      from_key+= HA_KEY_BLOB_LENGTH;
223
 
      field->move_field_offset(-ptrdiff);
224
 
    }
225
 
    else
226
 
    {
227
 
      length= cmin(key_length, key_part->length);
228
 
      /* skip the byte with 'uneven' bits, if used */
229
 
      memcpy(to_record + key_part->offset, from_key + used_uneven_bits
230
 
             , (size_t) length - used_uneven_bits);
231
 
    }
232
 
    from_key+= length;
233
 
    key_length-= length;
234
 
  }
235
 
}
236
 
 
237
 
 
238
 
/**
239
 
  Compare if a key has changed.
240
 
 
241
 
  @param table          Table
242
 
  @param key            key to compare to row
243
 
  @param idx            Index used
244
 
  @param key_length     Length of key
245
 
 
246
 
  @note
247
 
    In theory we could just call field->cmp() for all field types,
248
 
    but as we are only interested if a key has changed (not if the key is
249
 
    larger or smaller than the previous value) we can do things a bit
250
 
    faster by using memcmp() instead.
251
 
 
252
 
  @retval
253
 
    0   If key is equal
254
 
  @retval
255
 
    1   Key has changed
256
 
*/
257
 
 
258
 
bool key_cmp_if_same(Table *table,const unsigned char *key,uint32_t idx,uint32_t key_length)
259
 
{
260
 
  uint32_t store_length;
261
 
  KEY_PART_INFO *key_part;
262
 
  const unsigned char *key_end= key + key_length;;
263
 
 
264
 
  for (key_part=table->key_info[idx].key_part;
265
 
       key < key_end ; 
266
 
       key_part++, key+= store_length)
267
 
  {
268
 
    uint32_t length;
269
 
    store_length= key_part->store_length;
270
 
 
271
 
    if (key_part->null_bit)
272
 
    {
273
 
      if (*key != test(table->record[0][key_part->null_offset] & 
274
 
                       key_part->null_bit))
275
 
        return 1;
276
 
      if (*key)
277
 
        continue;
278
 
      key++;
279
 
      store_length--;
280
 
    }
281
 
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
282
 
                                   HA_BIT_PART))
283
 
    {
284
 
      if (key_part->field->key_cmp(key, key_part->length))
285
 
        return 1;
286
 
      continue;
287
 
    }
288
 
    length= cmin((uint) (key_end-key), store_length);
289
 
    if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
290
 
                                FIELDFLAG_PACK)))
291
 
    {
292
 
      const CHARSET_INFO * const cs= key_part->field->charset();
293
 
      uint32_t char_length= key_part->length / cs->mbmaxlen;
294
 
      const unsigned char *pos= table->record[0] + key_part->offset;
295
 
      if (length > char_length)
296
 
      {
297
 
        char_length= my_charpos(cs, pos, pos + length, char_length);
298
 
        set_if_smaller(char_length, length);
299
 
      }
300
 
      if (cs->coll->strnncollsp(cs,
301
 
                                (const unsigned char*) key, length,
302
 
                                (const unsigned char*) pos, char_length, 0))
303
 
        return 1;
304
 
      continue;
305
 
    }
306
 
    if (memcmp(key,table->record[0]+key_part->offset,length))
307
 
      return 1;
308
 
  }
309
 
  return 0;
310
 
}
311
 
 
312
 
/*
313
 
  unpack key-fields from record to some buffer.
314
 
 
315
 
  This is used mainly to get a good error message.  We temporary 
316
 
  change the column bitmap so that all columns are readable.
317
 
 
318
 
  @param
319
 
     to         Store value here in an easy to read form
320
 
  @param
321
 
     table      Table to use
322
 
  @param
323
 
     idx        Key number
324
 
*/
325
 
 
326
 
void key_unpack(String *to,Table *table,uint32_t idx)
327
 
{
328
 
  KEY_PART_INFO *key_part,*key_part_end;
329
 
  Field *field;
330
 
  String tmp;
331
 
 
332
 
  to->length(0);
333
 
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
334
 
         table->key_info[idx].key_parts ;
335
 
       key_part < key_part_end;
336
 
       key_part++)
337
 
  {
338
 
    if (to->length())
339
 
      to->append('-');
340
 
    if (key_part->null_bit)
341
 
    {
342
 
      if (table->record[0][key_part->null_offset] & key_part->null_bit)
343
 
      {
344
 
        to->append(STRING_WITH_LEN("NULL"));
345
 
        continue;
346
 
      }
347
 
    }
348
 
    if ((field=key_part->field))
349
 
    {
350
 
      const CHARSET_INFO * const cs= field->charset();
351
 
      field->val_str(&tmp);
352
 
      if (cs->mbmaxlen > 1 &&
353
 
          table->field[key_part->fieldnr - 1]->field_length !=
354
 
          key_part->length)
355
 
      {
356
 
        /*
357
 
          Prefix key, multi-byte charset.
358
 
          For the columns of type CHAR(N), the above val_str()
359
 
          call will return exactly "key_part->length" bytes,
360
 
          which can break a multi-byte characters in the middle.
361
 
          Align, returning not more than "char_length" characters.
362
 
        */
363
 
        uint32_t charpos, char_length= key_part->length / cs->mbmaxlen;
364
 
        if ((charpos= my_charpos(cs, tmp.ptr(),
365
 
                                 tmp.ptr() + tmp.length(),
366
 
                                 char_length)) < key_part->length)
367
 
          tmp.length(charpos);
368
 
      }
369
 
      
370
 
      if (key_part->length < field->pack_length())
371
 
        tmp.length(cmin(tmp.length(),(uint32_t)key_part->length));
372
 
      to->append(tmp);
373
 
    }
374
 
    else
375
 
      to->append(STRING_WITH_LEN("???"));
376
 
  }
377
 
 
378
 
  return;
379
 
}
380
 
 
381
 
 
382
 
/*
383
 
  Check if key uses field that is marked in passed field bitmap.
384
 
 
385
 
  SYNOPSIS
386
 
    is_key_used()
387
 
      table   Table object with which keys and fields are associated.
388
 
      idx     Key to be checked.
389
 
      fields  Bitmap of fields to be checked.
390
 
 
391
 
  NOTE
392
 
    This function uses Table::tmp_set bitmap so the caller should care
393
 
    about saving/restoring its state if it also uses this bitmap.
394
 
 
395
 
  RETURN VALUE
396
 
    TRUE   Key uses field from bitmap
397
 
    FALSE  Otherwise
398
 
*/
399
 
 
400
 
bool is_key_used(Table *table, uint32_t idx, const MY_BITMAP *fields)
401
 
{
402
 
  bitmap_clear_all(&table->tmp_set);
403
 
  table->mark_columns_used_by_index_no_reset(idx, &table->tmp_set);
404
 
  if (bitmap_is_overlapping(&table->tmp_set, fields))
405
 
    return 1;
406
 
 
407
 
  /*
408
 
    If table handler has primary key as part of the index, check that primary
409
 
    key is not updated
410
 
  */
411
 
  if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
412
 
      (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
413
 
    return is_key_used(table, table->s->primary_key, fields);
414
 
  return 0;
415
 
}
416
 
 
417
 
 
418
 
/**
419
 
  Compare key in row to a given key.
420
 
 
421
 
  @param key_part               Key part handler
422
 
  @param key                    Key to compare to value in table->record[0]
423
 
  @param key_length             length of 'key'
424
 
 
425
 
  @return
426
 
    The return value is SIGN(key_in_row - range_key):
427
 
    -   0               Key is equal to range or 'range' == 0 (no range)
428
 
    -  -1               Key is less than range
429
 
    -   1               Key is larger than range
430
 
*/
431
 
 
432
 
int key_cmp(KEY_PART_INFO *key_part, const unsigned char *key, uint32_t key_length)
433
 
{
434
 
  uint32_t store_length;
435
 
 
436
 
  for (const unsigned char *end=key + key_length;
437
 
       key < end;
438
 
       key+= store_length, key_part++)
439
 
  {
440
 
    int cmp;
441
 
    store_length= key_part->store_length;
442
 
    if (key_part->null_bit)
443
 
    {
444
 
      /* This key part allows null values; NULL is lower than everything */
445
 
      register bool field_is_null= key_part->field->is_null();
446
 
      if (*key)                                 // If range key is null
447
 
      {
448
 
        /* the range is expecting a null value */
449
 
        if (!field_is_null)
450
 
          return 1;                             // Found key is > range
451
 
        /* null -- exact match, go to next key part */
452
 
        continue;
453
 
      }
454
 
      else if (field_is_null)
455
 
        return -1;                              // NULL is less than any value
456
 
      key++;                                    // Skip null byte
457
 
      store_length--;
458
 
    }
459
 
    if ((cmp=key_part->field->key_cmp(key, key_part->length)) < 0)
460
 
      return -1;
461
 
    if (cmp > 0)
462
 
      return 1;
463
 
  }
464
 
  return 0;                                     // Keys are equal
465
 
}
466
 
 
467
 
 
468
 
/*
469
 
  Compare two records in index order
470
 
  SYNOPSIS
471
 
    key_rec_cmp()
472
 
    key                         Index information
473
 
    rec0                        Pointer to table->record[0]
474
 
    first_rec                   Pointer to record compare with
475
 
    second_rec                  Pointer to record compare against first_rec
476
 
 
477
 
  DESCRIPTION
478
 
    This method is set-up such that it can be called directly from the
479
 
    priority queue and it is attempted to be optimised as much as possible
480
 
    since this will be called O(N * log N) times while performing a merge
481
 
    sort in various places in the code.
482
 
 
483
 
    We retrieve the pointer to table->record[0] using the fact that key_parts
484
 
    have an offset making it possible to calculate the start of the record.
485
 
    We need to get the diff to the compared record since none of the records
486
 
    being compared are stored in table->record[0].
487
 
 
488
 
    We first check for NULL values, if there are no NULL values we use
489
 
    a compare method that gets two field pointers and a max length
490
 
    and return the result of the comparison.
491
 
*/
492
 
 
493
 
int key_rec_cmp(void *key, unsigned char *first_rec, unsigned char *second_rec)
494
 
{
495
 
  KEY *key_info= (KEY*)key;
496
 
  uint32_t key_parts= key_info->key_parts, i= 0;
497
 
  KEY_PART_INFO *key_part= key_info->key_part;
498
 
  unsigned char *rec0= key_part->field->ptr - key_part->offset;
499
 
  my_ptrdiff_t first_diff= first_rec - rec0, sec_diff= second_rec - rec0;
500
 
  int result= 0;
501
 
 
502
 
  do
503
 
  {
504
 
    Field *field= key_part->field;
505
 
 
506
 
    if (key_part->null_bit)
507
 
    {
508
 
      /* The key_part can contain NULL values */
509
 
      bool first_is_null= field->is_null_in_record_with_offset(first_diff);
510
 
      bool sec_is_null= field->is_null_in_record_with_offset(sec_diff);
511
 
      /*
512
 
        NULL is smaller then everything so if first is NULL and the other
513
 
        not then we know that we should return -1 and for the opposite
514
 
        we should return +1. If both are NULL then we call it equality
515
 
        although it is a strange form of equality, we have equally little
516
 
        information of the real value.
517
 
      */
518
 
      if (!first_is_null)
519
 
      {
520
 
        if (!sec_is_null)
521
 
          ; /* Fall through, no NULL fields */
522
 
        else
523
 
        {
524
 
          return(1);
525
 
        }
526
 
      }
527
 
      else if (!sec_is_null)
528
 
      {
529
 
        return(-1);
530
 
      }
531
 
      else
532
 
        goto next_loop; /* Both were NULL */
533
 
    }
534
 
    /*
535
 
      No null values in the fields
536
 
      We use the virtual method cmp_max with a max length parameter.
537
 
      For most field types this translates into a cmp without
538
 
      max length. The exceptions are the BLOB and VARCHAR field types
539
 
      that take the max length into account.
540
 
    */
541
 
    result= field->cmp_max(field->ptr+first_diff, field->ptr+sec_diff,
542
 
                           key_part->length);
543
 
next_loop:
544
 
    key_part++;
545
 
  } while (!result && ++i < key_parts);
546
 
  return(result);
547
 
}