diff -Narup ./017_mariadb-10.0.15-get_error_at_commit/include/hash.h ./018_mariadb-10.0.15-hash_tuning/include/hash.h
--- ./017_mariadb-10.0.15-get_error_at_commit/include/hash.h	2014-11-22 06:02:57.000000000 +0900
+++ ./018_mariadb-10.0.15-hash_tuning/include/hash.h	2014-11-29 17:04:43.654797027 +0900
@@ -22,6 +22,8 @@
 #include "my_global.h"                          /* uchar */
 #include "my_sys.h"                             /* DYNAMIC_ARRAY */
 
+#define HASH_UPDATE_WITH_HASH_VALUE
+
 /*
   This forward declaration is used from C files where the real
   definition is included before.  Since C does not allow repeated
@@ -90,10 +92,26 @@ uchar *my_hash_first_from_hash_value(con
                                      HASH_SEARCH_STATE *state);
 uchar *my_hash_next(const HASH *info, const uchar *key, size_t length,
                     HASH_SEARCH_STATE *state);
+uchar *my_hash_next_with_hash_value(const HASH *info,
+                                    my_hash_value_type hash_value,
+                                    const uchar *key, size_t length,
+                                    HASH_SEARCH_STATE *state);
 my_bool my_hash_insert(HASH *info, const uchar *data);
+my_bool my_hash_insert_with_hash_value(HASH *info,
+                                       my_hash_value_type hash_value,
+                                       const uchar *data);
 my_bool my_hash_delete(HASH *hash, uchar *record);
+my_bool my_hash_delete_with_hash_value(HASH *hash,
+                                       my_hash_value_type hash_value,
+                                       uchar *record);
 my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key,
                        size_t old_key_length);
+my_bool my_hash_update_with_hash_value(HASH *hash,
+                                       my_hash_value_type hash_value,
+                                       uchar *record,
+                                       my_hash_value_type old_hash_value,
+                                       uchar *old_key,
+                                       size_t old_key_length);
 void my_hash_replace(HASH *hash, HASH_SEARCH_STATE *state, uchar *new_row);
 my_bool my_hash_check(HASH *hash); /* Only in debug library */
 my_bool my_hash_iterate(HASH *hash, my_hash_walk_action action, void *argument);
diff -Narup ./017_mariadb-10.0.15-get_error_at_commit/mysys/hash.c ./018_mariadb-10.0.15-hash_tuning/mysys/hash.c
--- ./017_mariadb-10.0.15-get_error_at_commit/mysys/hash.c	2014-11-22 06:02:57.000000000 +0900
+++ ./018_mariadb-10.0.15-hash_tuning/mysys/hash.c	2014-11-29 17:04:43.656796311 +0900
@@ -32,6 +32,7 @@
 typedef struct st_hash_info {
   uint next;					/* index to next key */
   uchar *data;					/* data for current entry */
+  my_hash_value_type hash_value;
 } HASH_LINK;
 
 static uint my_hash_mask(my_hash_value_type hashnr,
@@ -39,6 +40,9 @@ static uint my_hash_mask(my_hash_value_t
 static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
 static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
                    size_t length);
+static int hashcmp_with_hash_value(const HASH *hash, HASH_LINK *pos,
+                                   my_hash_value_type hash_value,
+                                   const uchar *key, size_t length);
 
 my_hash_value_type my_hash_sort(const CHARSET_INFO *cs, const uchar *key,
                                 size_t length)
@@ -278,7 +282,7 @@ uchar* my_hash_first_from_hash_value(con
     do
     {
       pos= dynamic_element(&hash->array,idx,HASH_LINK*);
-      if (!hashcmp(hash,pos,key,length))
+      if (!hashcmp_with_hash_value(hash,pos,hash_value,key,length))
       {
 	DBUG_PRINT("exit",("found key at %d",idx));
 	*current_record= idx;
@@ -287,7 +291,7 @@ uchar* my_hash_first_from_hash_value(con
       if (flag)
       {
 	flag=0;					/* Reset flag */
-	if (my_hash_rec_mask(hash, pos, hash->blength, hash->records) != idx)
+	if (my_hash_mask(pos->hash_value, hash->blength, hash->records) != idx)
 	  break;				/* Wrong link */
       }
     }
@@ -324,6 +328,32 @@ uchar* my_hash_next(const HASH *hash, co
 }
 
 
+uchar* my_hash_next_with_hash_value(const HASH *hash,
+                                    my_hash_value_type hash_value,
+                                    const uchar *key, size_t length,
+                                    HASH_SEARCH_STATE *current_record)
+{
+  HASH_LINK *pos;
+  uint idx;
+
+  if (*current_record != NO_RECORD)
+  {
+    HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
+    for (idx=data[*current_record].next; idx != NO_RECORD ; idx=pos->next)
+    {
+      pos=data+idx;
+      if (!hashcmp_with_hash_value(hash,pos,hash_value,key,length))
+      {
+	*current_record= idx;
+	return pos->data;
+      }
+    }
+    *current_record= NO_RECORD;
+  }
+  return 0;
+}
+
+
 	/* Change link from pos to new_link */
 
 static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
@@ -368,6 +398,19 @@ static int hashcmp(const HASH *hash, HAS
 }
 
 
+static int hashcmp_with_hash_value(const HASH *hash, HASH_LINK *pos,
+                                   my_hash_value_type hash_value,
+                                   const uchar *key, size_t length)
+{
+  size_t rec_keylength;
+  uchar *rec_key= (uchar*) my_hash_key(hash, pos->data, &rec_keylength, 1);
+  return ((length && length != rec_keylength) ||
+          (pos->hash_value != hash_value) ||
+	  my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
+		       (uchar*) key, rec_keylength));
+}
+
+
 /**
    Write a hash-key to the hash-index
 
@@ -378,16 +421,24 @@ static int hashcmp(const HASH *hash, HAS
 
 my_bool my_hash_insert(HASH *info, const uchar *record)
 {
+  my_hash_value_type hash_value = rec_hashnr(info, record);
+  return(my_hash_insert_with_hash_value(info, hash_value, record));
+}
+
+my_bool my_hash_insert_with_hash_value(HASH *info,
+                                       my_hash_value_type hash_value,
+                                       const uchar *record)
+{
   int flag;
   size_t idx,halfbuff,first_index;
-  my_hash_value_type hash_nr;
+  my_hash_value_type hash_nr,ptr_to_rec_hash_value,ptr_to_rec2_hash_value;
   uchar *UNINIT_VAR(ptr_to_rec),*UNINIT_VAR(ptr_to_rec2);
   HASH_LINK *data,*empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos;
 
   if (info->flags & HASH_UNIQUE)
   {
     uchar *key= (uchar*) my_hash_key(info, record, &idx, 1);
-    if (my_hash_search(info, key, idx))
+    if (my_hash_search_using_hash_value(info, hash_value, key, idx))
       return(TRUE);				/* Duplicate entry */
   }
 
@@ -404,7 +455,7 @@ my_bool my_hash_insert(HASH *info, const
     do
     {
       pos=data+idx;
-      hash_nr=rec_hashnr(info,pos->data);
+      hash_nr= pos->hash_value;
       if (flag == 0)				/* First loop; Check if ok */
 	if (my_hash_mask(hash_nr, info->blength, info->records) != first_index)
 	  break;
@@ -418,6 +469,7 @@ my_bool my_hash_insert(HASH *info, const
 	    /* key shall be moved to the current empty position */
 	    gpos=empty;
 	    ptr_to_rec=pos->data;
+	    ptr_to_rec_hash_value=pos->hash_value;
 	    empty=pos;				/* This place is now free */
 	  }
 	  else
@@ -425,6 +477,7 @@ my_bool my_hash_insert(HASH *info, const
 	    flag=LOWFIND | LOWUSED;		/* key isn't changed */
 	    gpos=pos;
 	    ptr_to_rec=pos->data;
+	    ptr_to_rec_hash_value=pos->hash_value;
 	  }
 	}
 	else
@@ -433,11 +486,13 @@ my_bool my_hash_insert(HASH *info, const
 	  {
 	    /* Change link of previous LOW-key */
 	    gpos->data=ptr_to_rec;
+	    gpos->hash_value=ptr_to_rec_hash_value;
 	    gpos->next= (uint) (pos-data);
 	    flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
 	  }
 	  gpos=pos;
 	  ptr_to_rec=pos->data;
+	  ptr_to_rec_hash_value=pos->hash_value;
 	}
       }
       else
@@ -448,6 +503,7 @@ my_bool my_hash_insert(HASH *info, const
 	  /* key shall be moved to the last (empty) position */
 	  gpos2 = empty; empty=pos;
 	  ptr_to_rec2=pos->data;
+	  ptr_to_rec2_hash_value=pos->hash_value;
 	}
 	else
 	{
@@ -455,11 +511,13 @@ my_bool my_hash_insert(HASH *info, const
 	  {
 	    /* Change link of previous hash-key and save */
 	    gpos2->data=ptr_to_rec2;
+	    gpos2->hash_value=ptr_to_rec2_hash_value;
 	    gpos2->next=(uint) (pos-data);
 	    flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
 	  }
 	  gpos2=pos;
 	  ptr_to_rec2=pos->data;
+	  ptr_to_rec2_hash_value=pos->hash_value;
 	}
       }
     }
@@ -468,36 +526,42 @@ my_bool my_hash_insert(HASH *info, const
     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
     {
       gpos->data=ptr_to_rec;
+      gpos->hash_value=ptr_to_rec_hash_value;
       gpos->next=NO_RECORD;
     }
     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
     {
       gpos2->data=ptr_to_rec2;
+      gpos2->hash_value=ptr_to_rec2_hash_value;
       gpos2->next=NO_RECORD;
     }
   }
   /* Check if we are at the empty position */
 
-  idx= my_hash_mask(rec_hashnr(info, record), info->blength, info->records + 1);
+  idx= my_hash_mask(hash_value, info->blength, info->records + 1);
   pos=data+idx;
   if (pos == empty)
   {
     pos->data=(uchar*) record;
+    pos->hash_value= hash_value;
     pos->next=NO_RECORD;
   }
   else
   {
     /* Check if more records in same hash-nr family */
     empty[0]=pos[0];
-    gpos= data + my_hash_rec_mask(info, pos, info->blength, info->records + 1);
+    gpos= data + my_hash_mask(pos->hash_value, info->blength,
+                              info->records + 1);
     if (pos == gpos)
     {
       pos->data=(uchar*) record;
+      pos->hash_value= hash_value;
       pos->next=(uint) (empty - data);
     }
     else
     {
       pos->data=(uchar*) record;
+      pos->hash_value= hash_value;
       pos->next=NO_RECORD;
       movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
     }
@@ -526,18 +590,28 @@ my_bool my_hash_insert(HASH *info, const
 
 my_bool my_hash_delete(HASH *hash, uchar *record)
 {
+  my_hash_value_type hash_value;
+  DBUG_ENTER("my_hash_delete");
+  hash_value = rec_hashnr(hash, record);
+  DBUG_RETURN(my_hash_delete_with_hash_value(hash, hash_value, record));
+}
+
+my_bool my_hash_delete_with_hash_value(HASH *hash,
+                                       my_hash_value_type hash_value,
+                                       uchar *record)
+{
   uint pos2,idx,empty_index;
   my_hash_value_type pos_hashnr, lastpos_hashnr;
   size_t blength;
   HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
-  DBUG_ENTER("my_hash_delete");
+  DBUG_ENTER("my_hash_delete_with_hash_value");
   if (!hash->records)
     DBUG_RETURN(1);
 
   blength=hash->blength;
   data=dynamic_element(&hash->array,0,HASH_LINK*);
   /* Search after record with key */
-  pos= data + my_hash_mask(rec_hashnr(hash, record), blength, hash->records);
+  pos= data + my_hash_mask(hash_value, blength, hash->records);
   gpos = 0;
 
   while (pos->data != record)
@@ -559,6 +633,7 @@ my_bool my_hash_delete(HASH *hash, uchar
   {
     empty=data+(empty_index=pos->next);
     pos->data=empty->data;
+    pos->hash_value=empty->hash_value;
     pos->next=empty->next;
   }
 
@@ -566,7 +641,7 @@ my_bool my_hash_delete(HASH *hash, uchar
     goto exit;
 
   /* Move the last key (lastpos) */
-  lastpos_hashnr=rec_hashnr(hash,lastpos->data);
+  lastpos_hashnr=lastpos->hash_value;
   /* pos is where lastpos should be */
   pos= data + my_hash_mask(lastpos_hashnr, hash->blength, hash->records);
   if (pos == empty)			/* Move to empty position. */
@@ -574,7 +649,7 @@ my_bool my_hash_delete(HASH *hash, uchar
     empty[0]=lastpos[0];
     goto exit;
   }
-  pos_hashnr=rec_hashnr(hash,pos->data);
+  pos_hashnr=pos->hash_value;
   /* pos3 is where the pos should be */
   pos3= data + my_hash_mask(pos_hashnr, hash->blength, hash->records);
   if (pos != pos3)
@@ -617,23 +692,45 @@ exit:
 my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key,
                        size_t old_key_length)
 {
+  my_hash_value_type hash_value;
+  my_hash_value_type old_hash_value;
+  DBUG_ENTER("my_hash_update");
+  hash_value = rec_hashnr(hash, record);
+  old_hash_value = calc_hash(hash, old_key,
+                             (old_key_length ?
+                              old_key_length :
+                              hash->key_length));
+  DBUG_RETURN(my_hash_update_with_hash_value(hash, hash_value, record,
+                                             old_hash_value,
+                                             old_key, old_key_length));
+}
+
+my_bool my_hash_update_with_hash_value(HASH *hash,
+                                       my_hash_value_type hash_value,
+                                       uchar *record,
+                                       my_hash_value_type old_hash_value,
+                                       uchar *old_key,
+                                       size_t old_key_length)
+{
   uint new_index,new_pos_index,records;
   size_t idx, empty, blength;
   HASH_LINK org_link,*data,*previous,*pos;
-  DBUG_ENTER("my_hash_update");
+  DBUG_ENTER("my_hash_update_with_hash_value");
   
   if (HASH_UNIQUE & hash->flags)
   {
     HASH_SEARCH_STATE state;
     uchar *found, *new_key= (uchar*) my_hash_key(hash, record, &idx, 1);
-    if ((found= my_hash_first(hash, new_key, idx, &state)))
+    if ((found= my_hash_first_from_hash_value(hash, hash_value, new_key, idx,
+                                              &state)))
     {
       do 
       {
         if (found != record)
           DBUG_RETURN(1);		/* Duplicate entry */
       } 
-      while ((found= my_hash_next(hash, new_key, idx, &state)));
+      while ((found= my_hash_next_with_hash_value(hash, hash_value, new_key,
+                                                  idx, &state)));
     }
   }
 
@@ -642,11 +739,8 @@ my_bool my_hash_update(HASH *hash, uchar
 
   /* Search after record with key */
 
-  idx= my_hash_mask(hash->hash_function(hash->charset, old_key,
-                                        (old_key_length ? old_key_length :
-                                                          hash->key_length)),
-                    blength, records);
-  new_index= my_hash_mask(rec_hashnr(hash, record), blength, records);
+  idx= my_hash_mask(old_hash_value, blength, records);
+  new_index= my_hash_mask(hash_value, blength, records);
   if (idx == new_index)
     DBUG_RETURN(0);			/* Nothing to do (No record check) */
   previous=0;
@@ -696,7 +790,7 @@ my_bool my_hash_update(HASH *hash, uchar
     DBUG_RETURN(0);
   }
   pos=data+new_index;
-  new_pos_index= my_hash_rec_mask(hash, pos, blength, records);
+  new_pos_index= my_hash_mask(pos->hash_value, blength, records);
   if (new_index != new_pos_index)
   {					/* Other record in wrong position */
     data[empty] = *pos;
@@ -730,8 +824,12 @@ uchar *my_hash_element(HASH *hash, ulong
 void my_hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record,
                      uchar *new_row)
 {
-  if (*current_record != NO_RECORD)            /* Safety */
-    dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;
+  if (*current_record != NO_RECORD) {           /* Safety */
+    HASH_LINK *pos =
+      dynamic_element(&hash->array, *current_record, HASH_LINK*);
+    pos->data= new_row;
+    pos->hash_value= rec_hashnr(hash, new_row);
+  }
 }
 
 
