diff options
Diffstat (limited to 'sql/key.cc')
-rw-r--r-- | sql/key.cc | 208 |
1 files changed, 153 insertions, 55 deletions
diff --git a/sql/key.cc b/sql/key.cc index c092a543072..700fd51bd86 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -17,41 +17,57 @@ /* Functions to handle keys and fields in forms */ #include "mysql_priv.h" -#include "sql_trigger.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) + +/* + Search after a key that starts with 'field' + + SYNOPSIS + find_ref_key() + key First key to check + key_count How many keys to check + record Start of record + field Field to search after + key_length On partial match, contains length of fields before + field + + NOTES + Used when calculating key for NEXT_NUMBER + + IMPLEMENTATION + If no key starts with field test if field is part of some key. If we find + one, then return first key and set key_length to the number of bytes + preceding 'field'. + + RETURN + -1 field is not part of the key + # Key part for key matching key. + key_length is set to length of key before (not including) field +*/ + +int find_ref_key(KEY *key, uint key_count, byte *record, Field *field, + uint *key_length) { reg2 int i; reg3 KEY *key_info; uint fieldpos; - fieldpos= field->offset(); - - /* Test if some key starts as fieldpos */ + fieldpos= field->offset(record); - for (i= 0, key_info= table->key_info ; - i < (int) table->s->keys ; + /* Test if some key starts as fieldpos */ + for (i= 0, key_info= key ; + i < (int) key_count ; i++, key_info++) { if (key_info->key_part[0].offset == fieldpos) - { /* Found key. Calc keylength */ + { /* Found key. Calc keylength */ *key_length=0; - return(i); /* Use this key */ + return(i); /* Use this key */ } } - /* Test if some key contains fieldpos */ - for (i= 0, key_info= table->key_info ; - i < (int) table->s->keys ; + /* Test if some key contains fieldpos */ + for (i= 0, key_info= key; + i < (int) key_count ; i++, key_info++) { uint j; @@ -62,7 +78,7 @@ int find_ref_key(TABLE *table,Field *field, uint *key_length) j++, key_part++) { if (key_part->offset == fieldpos) - return(i); /* Use this key */ + return(i); /* Use this key */ *key_length+=key_part->store_length; } } @@ -210,9 +226,13 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info, } else if (key_part->key_part_flag & HA_VAR_LENGTH_PART) { + my_bitmap_map *old_map; key_length-= HA_KEY_BLOB_LENGTH; length= min(key_length, key_part->length); + old_map= dbug_tmp_use_all_columns(key_part->field->table, + key_part->field->table->write_set); key_part->field->set_key_image((char *) from_key, length); + dbug_tmp_restore_column_map(key_part->field->table->write_set, old_map); from_key+= HA_KEY_BLOB_LENGTH; } else @@ -301,14 +321,26 @@ bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length) return 0; } - /* unpack key-fields from record to some buffer */ - /* This is used to get a good error message */ +/* + unpack key-fields from record to some buffer + + SYNOPSIS + key_unpack() + to Store value here in an easy to read form + table Table to use + idx Key number + + NOTES + This is used mainly to get a good error message + We temporary change the column bitmap so that all columns are readable. +*/ void key_unpack(String *to,TABLE *table,uint idx) { KEY_PART_INFO *key_part,*key_part_end; Field *field; String tmp; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); DBUG_ENTER("key_unpack"); to->length(0); @@ -337,58 +369,42 @@ void key_unpack(String *to,TABLE *table,uint idx) else to->append(STRING_WITH_LEN("???")); } + dbug_tmp_restore_column_map(table->read_set, old_map); DBUG_VOID_RETURN; } /* - Check if key uses field that is listed in passed field list or is - automatically updated (like a timestamp) or can be updated by before - update trigger defined on the table. + Check if key uses field that is marked in passed field bitmap. SYNOPSIS is_key_used() table TABLE object with which keys and fields are associated. idx Key to be checked. - fields List of fields to be checked. + fields Bitmap of fields to be checked. + + NOTE + This function uses TABLE::tmp_set bitmap so the caller should care + about saving/restoring its state if it also uses this bitmap. RETURN VALUE - TRUE Key uses field which meets one the above conditions + TRUE Key uses field from bitmap FALSE Otherwise */ -bool is_key_used(TABLE *table, uint idx, List<Item> &fields) +bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields) { - Table_triggers_list *triggers= table->triggers; - List_iterator_fast<Item> f(fields); - 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; - - if (key_part->field == table->timestamp_field) - return 1; // Can't be used for update - - f.rewind(); - while ((field=(Item_field*) f++)) - { - if (key_part->field->eq(field->field)) - return 1; - } - if (triggers && - triggers->is_updated_in_before_update_triggers(key_part->field)) - return 1; - } + bitmap_clear_all(&table->tmp_set); + table->mark_columns_used_by_index_no_reset(idx, &table->tmp_set); + if (bitmap_is_overlapping(&table->tmp_set, fields)) + return 1; /* If table handler has primary key as part of the index, check that primary key is not updated */ if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY && - (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) + (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) return is_key_used(table, table->s->primary_key, fields); return 0; } @@ -445,3 +461,85 @@ int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length) } return 0; // Keys are equal } + + +/* + Compare two records in index order + SYNOPSIS + key_rec_cmp() + key Index information + rec0 Pointer to table->record[0] + first_rec Pointer to record compare with + second_rec Pointer to record compare against first_rec + DESCRIPTION + This method is set-up such that it can be called directly from the + priority queue and it is attempted to be optimised as much as possible + since this will be called O(N * log N) times while performing a merge + sort in various places in the code. + + We retrieve the pointer to table->record[0] using the fact that key_parts + have an offset making it possible to calculate the start of the record. + We need to get the diff to the compared record since none of the records + being compared are stored in table->record[0]. + + We first check for NULL values, if there are no NULL values we use + a compare method that gets two field pointers and a max length + and return the result of the comparison. +*/ + +int key_rec_cmp(void *key, byte *first_rec, byte *second_rec) +{ + KEY *key_info= (KEY*)key; + uint key_parts= key_info->key_parts, i= 0; + KEY_PART_INFO *key_part= key_info->key_part; + char *rec0= key_part->field->ptr - key_part->offset; + my_ptrdiff_t first_diff= first_rec - (byte*)rec0, sec_diff= second_rec - (byte*)rec0; + int result= 0; + DBUG_ENTER("key_rec_cmp"); + + do + { + Field *field= key_part->field; + + if (key_part->null_bit) + { + /* The key_part can contain NULL values */ + bool first_is_null= field->is_null_in_record_with_offset(first_diff); + bool sec_is_null= field->is_null_in_record_with_offset(sec_diff); + /* + NULL is smaller then everything so if first is NULL and the other + not then we know that we should return -1 and for the opposite + we should return +1. If both are NULL then we call it equality + although it is a strange form of equality, we have equally little + information of the real value. + */ + if (!first_is_null) + { + if (!sec_is_null) + ; /* Fall through, no NULL fields */ + else + { + DBUG_RETURN(+1); + } + } + else if (!sec_is_null) + { + DBUG_RETURN(-1); + } + else + goto next_loop; /* Both were NULL */ + } + /* + No null values in the fields + We use the virtual method cmp_max with a max length parameter. + For most field types this translates into a cmp without + max length. The exceptions are the BLOB and VARCHAR field types + that take the max length into account. + */ + result= field->cmp_max(field->ptr+first_diff, field->ptr+sec_diff, + key_part->length); +next_loop: + key_part++; + } while (!result && ++i < key_parts); + DBUG_RETURN(result); +} |