diff --git a/include/my_base.h b/include/my_base.h
index b806436fe0e..167c062deb5 100644
--- a/include/my_base.h
+++ b/include/my_base.h
@@ -123,7 +123,8 @@ enum ha_extra_function {
   HA_EXTRA_NO_IGNORE_DUP_KEY,
   HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE,	/* Cursor will not be used for update */
   HA_EXTRA_PREPARE_FOR_DELETE,
-  HA_EXTRA_PREPARE_FOR_UPDATE		/* Remove read cache if problems */
+  HA_EXTRA_PREPARE_FOR_UPDATE,		/* Remove read cache if problems */
+  HA_EXTRA_PRELOAD_BUFFER_SIZE          /* Set buffer size for preloading */
 };
 
 	/* The following is parameter to ha_panic() */
@@ -255,6 +256,7 @@ enum ha_base_keytype {
 #define HA_ERR_CANNOT_ADD_FOREIGN 150    /* Cannot add a foreign key constr. */
 #define HA_ERR_NO_REFERENCED_ROW 151     /* Cannot add a child row */
 #define HA_ERR_ROW_IS_REFERENCED 152     /* Cannot delete a parent row */
+#define HA_ERR_NON_UNIQUE_BLOCK_SIZE 153 /* Non unique key block size */
 
 	/* Other constants */
 
diff --git a/include/my_sys.h b/include/my_sys.h
index 86ca6ec6a1f..75a7627dc07 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -644,6 +644,8 @@ extern int init_key_cache(ulong use_mem);
 extern int resize_key_cache(ulong use_mem);
 extern byte *key_cache_read(File file,my_off_t filepos,byte* buff,uint length,
 			    uint block_length,int return_buffer);
+extern int key_cache_insert(File file, my_off_t filepos,
+                            byte *buff, uint length);
 extern int key_cache_write(File file,my_off_t filepos,byte* buff,uint length,
 			   uint block_length,int force_write);
 extern int flush_key_blocks(int file, enum flush_type type);
diff --git a/include/myisam.h b/include/myisam.h
index 33aa6aa3f31..e85d3057672 100644
--- a/include/myisam.h
+++ b/include/myisam.h
@@ -410,6 +410,7 @@ my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, ulonglong key_map,
 int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows);
 void mi_flush_bulk_insert(MI_INFO *info, uint inx);
 void mi_end_bulk_insert(MI_INFO *info);
+int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves);
 
 #ifdef	__cplusplus
 }
diff --git a/myisam/Makefile.am b/myisam/Makefile.am
index f8225868d96..d4cd953ac66 100644
--- a/myisam/Makefile.am
+++ b/myisam/Makefile.am
@@ -47,6 +47,7 @@ libmyisam_a_SOURCES =	mi_open.c mi_extra.c mi_info.c mi_rkey.c \
 			mi_range.c mi_dbug.c mi_checksum.c mi_log.c \
 			mi_changed.c mi_static.c mi_delete_all.c \
 			mi_delete_table.c mi_rename.c  mi_check.c \
+			mi_preload.c \
 			ft_parser.c ft_stopwords.c ft_static.c \
 			ft_update.c ft_boolean_search.c ft_nlq_search.c sort.c \
 			rt_index.c rt_key.c rt_mbr.c rt_split.c sp_key.c
diff --git a/myisam/mi_extra.c b/myisam/mi_extra.c
index 75057dd4e6a..4b71f3a867b 100644
--- a/myisam/mi_extra.c
+++ b/myisam/mi_extra.c
@@ -367,6 +367,9 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
     if (!share->state.header.uniques)
       info->opt_flag|= OPT_NO_ROWS;
     break;
+  case HA_EXTRA_PRELOAD_BUFFER_SIZE:
+    info->preload_buff_size= *((ulong *) extra_arg); 
+    break;
   case HA_EXTRA_KEY_CACHE:
   case HA_EXTRA_NO_KEY_CACHE:
   default:
diff --git a/myisam/mi_preload.c b/myisam/mi_preload.c
new file mode 100644
index 00000000000..be45be66ecf
--- /dev/null
+++ b/myisam/mi_preload.c
@@ -0,0 +1,118 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+   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.
+
+   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.
+
+   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 */
+
+/*
+  Preload indexes into key cache
+*/
+
+#include "myisamdef.h"
+
+
+/*
+  Preload pages of the index file for a table into the key cache
+
+  SYNOPSIS
+    mi_preload()
+      info          open table
+      map           map of indexes to preload into key cache 
+      ignore_leaves only non-leaves pages are to be preloaded
+
+  RETURN VALUE
+    0 if a success. error code - otherwise.
+
+  NOTES.
+    At present pages for all indexes are preloaded.
+    In future only pages for indexes specified in the key_map parameter
+    of the table will be preloaded.
+*/
+
+int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves)
+{
+  uint i;
+  uint length;
+  uint block_length= 0;
+  uchar *buff= NULL;
+  MYISAM_SHARE* share= info->s;
+  uint keys= share->state.header.keys;
+  MI_KEYDEF *keyinfo= share->keyinfo;
+  my_off_t key_file_length= share->state.state.key_file_length;
+  my_off_t pos= share->base.keystart;
+
+  if (!keys || !key_map || key_file_length == pos)
+    return 0;
+
+  block_length= keyinfo[0].block_length;
+
+  if (!key_map)
+    return 0;
+
+  /* Check whether all indexes use the same block size */
+  for (i= 1 ; i < keys ; i++)
+  {
+    if (keyinfo[i].block_length != block_length)
+      return (my_errno= HA_ERR_NON_UNIQUE_BLOCK_SIZE); 
+  }
+
+  length= info->preload_buff_size/block_length * block_length;
+  set_if_bigger(length, block_length);
+
+  if (!(buff= (uchar *) my_malloc(length, MYF(MY_WME))))
+    return (my_errno= HA_ERR_OUT_OF_MEM);
+
+  if (flush_key_blocks(share->kfile, FLUSH_RELEASE))
+    goto err;
+ 
+  do
+  {
+    /* Read the next block of index file into the preload buffer */
+    set_if_smaller(length, key_file_length-pos);
+    if (my_pread(share->kfile, (byte*) buff, length, pos, MYF(MY_FAE)))
+      goto err;
+          
+    if (ignore_leaves)
+    {
+      uchar *end= buff+length;
+      do
+      {
+        if (mi_test_if_nod(buff))
+        {
+          if (key_cache_insert(share->kfile, pos, (byte*) buff, block_length))
+	    goto err;
+	}
+        pos+= block_length;
+      }
+      while ((buff+= block_length) != end);
+      buff= end-length;
+    }
+    else
+    {
+      if (key_cache_insert(share->kfile, pos, (byte*) buff, length))
+	goto err;
+      pos+= length;
+    }
+
+  }
+  while (pos != key_file_length);
+
+  my_free(buff, MYF(0));
+
+  return 0;
+
+err:
+  my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
+  return (my_errno= errno);
+}
+
diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h
index 50320f1ecbd..9321a3918e2 100644
--- a/myisam/myisamdef.h
+++ b/myisam/myisamdef.h
@@ -262,6 +262,7 @@ struct st_myisam_info {
   int	save_lastinx;
   LIST	open_list;
   IO_CACHE rec_cache;			/* When cacheing records */
+  uint  preload_buff_size;              /* When preloading indexes */
   myf lock_wait;			/* is 0 or MY_DONT_WAIT */
   my_bool was_locked;			/* Was locked in panic */
   my_bool quick_mode;
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 264037e9a70..3276044fc2f 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -1258,6 +1258,86 @@ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length,
 }
 
 
+/*
+  Insert a block of file data from a buffer into key cache
+
+  SYNOPSIS
+    key_cache_insert()
+      file      file descriptor
+      filepos   file offset of the data from the buffer 
+      buff      buffer with data to insert into key cache
+      length    length of the data in the buffer
+
+  RETURN VALUE
+    0 if a success, 1 -otherwise.
+*/
+
+int key_cache_insert(File file, my_off_t filepos, byte *buff, uint length)
+{
+  DBUG_ENTER("key_cache_insert");
+  DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
+               (uint) file,(ulong) filepos, length));
+
+  if (my_disk_blocks > 0)
+  {
+    /* Key cache is used */
+    reg1 BLOCK_LINK *block;
+    uint offset= (uint) (filepos & (key_cache_block_size-1));
+    uint read_length;
+    int page_st;
+
+    /* Read data into key cache from buff in key_cache_block_size increments */
+    filepos-= offset;
+    do
+    {
+      read_length= length > key_cache_block_size ?
+                   key_cache_block_size : length;
+      KEYCACHE_DBUG_ASSERT(read_length > 0);
+      keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+      my_cache_r_requests++;
+      block=find_key_block(file, filepos, 0, &page_st);
+      if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
+      {
+        /* The requested page is to be read into the block buffer */
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+        keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+#endif
+
+        /* Copy data from buff */
+        if (!(read_length & 511))
+          bmove512(block->buffer+offset, buff, read_length);
+        else
+          memcpy(block->buffer+offset, buff, (size_t) read_length);
+
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+        keycache_pthread_mutex_lock(&THR_LOCK_keycache);
+#endif
+        block->status= BLOCK_READ;
+        block->length= read_length+offset;
+      }
+
+      remove_reader(block);
+      /*
+         Link the block into the LRU chain
+         if it's the last submitted request for the block
+      */
+      unreg_request(block,1);
+
+      keycache_pthread_mutex_unlock(&THR_LOCK_keycache);
+
+      if (block->status & BLOCK_ERROR)
+        DBUG_RETURN(1);
+
+      buff+=read_length;
+      filepos+=read_length;
+      offset=0;
+
+    } while ((length-= read_length));
+  }
+  DBUG_RETURN(0);
+}
+
+
 /*
   Write a buffer into disk;
   filepos must be a multiple of 'block_length', but it doesn't
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 213f5baf388..d9b9f3130de 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -674,6 +674,72 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool optimize)
 }
 
 
+/*
+  Preload pages of the index file for a table into the key cache.
+*/
+
+int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
+{
+  int error;
+  const char *errmsg;
+  ulonglong map= ~(ulonglong) 0;
+  TABLE_LIST *table_list= table->pos_in_table_list;
+  my_bool ignore_leaves= table_list->ignore_leaves;
+
+  DBUG_ENTER("ha_myisam::preload_keys");
+
+  /* Check validity of the index references */ 
+  if (table_list->use_index)
+  {
+    key_map kmap= get_key_map_from_key_list(table, table_list->use_index);
+    if (kmap == ~(key_map) 0)
+    {
+      errmsg= thd->net.last_error;
+      error= HA_ADMIN_FAILED;
+      goto err;
+    }
+    if (kmap)
+      map= kmap;
+  }
+  
+  mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
+           (void *) &thd->variables.preload_buff_size);
+
+  if ((error= mi_preload(file, map, ignore_leaves)))
+  {
+    switch (error) {
+    case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
+      errmsg= "Indexes use different block sizes";
+      break;
+    case HA_ERR_OUT_OF_MEM:
+      errmsg= "Failed to allocate buffer";
+      break;
+    default: 
+      char buf[ERRMSGSIZE+20];
+      my_snprintf(buf, ERRMSGSIZE, 
+                  "Failed to read from index file (errno: %d)", my_errno);
+      errmsg= buf;
+    }
+    error= HA_ADMIN_FAILED;
+    goto err;
+  }
+  
+  DBUG_RETURN(HA_ADMIN_OK);
+
+ err:
+  {
+    MI_CHECK param;
+    myisamchk_init(&param);
+    param.thd= thd;
+    param.op_name= (char*)"preload_keys";
+    param.db_name= table->table_cache_key;
+    param.table_name= table->table_name;
+    param.testflag= 0;
+    mi_check_print_error(&param, errmsg);
+    DBUG_RETURN(error);
+  }
+}
+
 /*
   Deactive all not unique index that can be recreated fast
 
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
index 154429ecc0d..8486e25556b 100644
--- a/sql/ha_myisam.h
+++ b/sql/ha_myisam.h
@@ -127,6 +127,7 @@ class ha_myisam: public handler
   int optimize(THD* thd, HA_CHECK_OPT* check_opt);
   int restore(THD* thd, HA_CHECK_OPT* check_opt);
   int backup(THD* thd, HA_CHECK_OPT* check_opt);
+  int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
 #ifdef HAVE_REPLICATION
   int dump(THD* thd, int fd);
   int net_read_dump(NET* net);
diff --git a/sql/handler.cc b/sql/handler.cc
index f2b7dbdf531..e84c9bf3569 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -588,6 +588,11 @@ int handler::analyze(THD* thd, HA_CHECK_OPT* check_opt)
   return HA_ADMIN_NOT_IMPLEMENTED;
 }
 
+int handler::preload_keys(THD* thd, HA_CHECK_OPT* check_opt)
+{
+  return HA_ADMIN_NOT_IMPLEMENTED;
+}
+
 /*
   Read first row (only) from a table
   This is never called for InnoDB or BDB tables, as these table types
diff --git a/sql/handler.h b/sql/handler.h
index 8c23a3625e0..a2408ab0fe0 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -304,6 +304,7 @@ public:
   virtual int optimize(THD* thd,HA_CHECK_OPT* check_opt);
   virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt);
   virtual int backup(THD* thd, HA_CHECK_OPT* check_opt);
+  virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
   /*
     restore assumes .frm file must exist, and that generate_table() has been
     called; It will just copy the data file and run repair.
diff --git a/sql/lex.h b/sql/lex.h
index f85431aadf7..0cbc048e00c 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -221,6 +221,7 @@ static SYMBOL symbols[] = {
   { "KILL",		SYM(KILL_SYM),0,0},
   { "LAST",		SYM(LAST_SYM),0,0},
   { "LEADING",		SYM(LEADING),0,0},
+  { "LEAVES",		SYM(LEAVES),0,0},
   { "LEFT",		SYM(LEFT),0,0},
   { "LEVEL",		SYM(LEVEL_SYM),0,0},
   { "LIKE",		SYM(LIKE),0,0},
@@ -299,6 +300,7 @@ static SYMBOL symbols[] = {
   { "POLYGON",		SYM(POLYGON),0,0},
   { "PURGE",		SYM(PURGE),0,0},
   { "PRECISION",	SYM(PRECISION),0,0},
+  { "PRELOAD",          SYM(PRELOAD),0,0},
   { "PREV",		SYM(PREV_SYM),0,0},
   { "PRIMARY",		SYM(PRIMARY_SYM),0,0},
   { "PROCEDURE",	SYM(PROCEDURE),0,0},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ea6e544a1fd..e0c3f5aeaec 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -246,6 +246,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout);
 /* Options to add_table_to_list() */
 #define TL_OPTION_UPDATING	1
 #define TL_OPTION_FORCE_INDEX	2
+#define TL_OPTION_IGNORE_LEAVES 4
 
 /* Some portable defines */
 
@@ -393,6 +394,8 @@ int mysql_analyze_table(THD* thd, TABLE_LIST* table_list,
 			HA_CHECK_OPT* check_opt);
 int mysql_optimize_table(THD* thd, TABLE_LIST* table_list,
 			 HA_CHECK_OPT* check_opt);
+int mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
+
 bool check_simple_select();
 
 SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length);
@@ -584,6 +587,8 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
 extern const Item **not_found_item;
 Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
 			  find_item_error_report_type report_error);
+key_map get_key_map_from_key_list(TABLE *table, 
+				  List<String> *index_list);
 bool insert_fields(THD *thd,TABLE_LIST *tables,
 		   const char *db_name, const char *table_name,
 		   List_iterator<Item> *it);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f73bd6721f0..7f3c69cb7c9 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3464,6 +3464,7 @@ enum options
   OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT,
   OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT,
   OPT_OPEN_FILES_LIMIT,
+  OPT_PRELOAD_BUFFER_SIZE,
   OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE,
   OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER,
   OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE,
@@ -4244,6 +4245,11 @@ Disable with --skip-isam",
    "If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of files.",
    (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG,
    REQUIRED_ARG, 0, 0, 65535, 0, 1, 0},
+   {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
+    "The size of the buffer that is allocated when preloading indexes",
+    (gptr*) &global_system_variables.preload_buff_size,
+    (gptr*) &max_system_variables.preload_buff_size, 0, GET_ULONG,
+    REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0},
 #ifdef HAVE_QUERY_CACHE
   {"query_cache_limit", OPT_QUERY_CACHE_LIMIT,
    "Don't cache results that are bigger than this.",
diff --git a/sql/set_var.cc b/sql/set_var.cc
index e4adbb0a318..ddaef4c4e14 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -199,6 +199,8 @@ sys_var_thd_ulong	sys_net_retry_count("net_retry_count",
 					    &SV::net_retry_count,
 					    fix_net_retry_count);
 sys_var_thd_bool	sys_new_mode("new", &SV::new_mode);
+sys_var_thd_ulong       sys_preload_buff_size("preload_buffer_size",
+                                              &SV::preload_buff_size);
 sys_var_thd_ulong	sys_read_buff_size("read_buffer_size",
 					   &SV::read_buff_size);
 sys_var_thd_ulong	sys_read_rnd_buff_size("read_rnd_buffer_size",
@@ -402,6 +404,7 @@ sys_var *sys_variables[]=
   &sys_net_wait_timeout,
   &sys_net_write_timeout,
   &sys_new_mode,
+  &sys_preload_buff_size,
   &sys_pseudo_thread_id,
   &sys_query_cache_size,
 #ifdef HAVE_QUERY_CACHE
@@ -573,6 +576,7 @@ struct show_var_st init_vars[]= {
   {"log_error",               (char*) log_error_file,               SHOW_CHAR},
   {"port",                    (char*) &mysql_port,                  SHOW_INT},
   {"protocol_version",        (char*) &protocol_version,            SHOW_INT},
+  {sys_preload_buff_size.name, (char*) &sys_preload_buff_size,      SHOW_SYS},
   {sys_pseudo_thread_id.name, (char*) &sys_pseudo_thread_id,        SHOW_SYS},
   {sys_read_buff_size.name,   (char*) &sys_read_buff_size,	    SHOW_SYS},
   {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size,	    SHOW_SYS},
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index fc95f5deb40..c7db77e1d84 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -35,8 +35,6 @@ static int open_unireg_entry(THD *thd,TABLE *entry,const char *db,
 			     const char *name, const char *alias);
 static void free_cache_entry(TABLE *entry);
 static void mysql_rm_tmp_tables(void);
-static key_map get_key_map_from_key_list(TABLE *table,
-					 List<String> *index_list);
 
 
 extern "C" byte *table_cache_key(const byte *record,uint *length,
@@ -2058,8 +2056,8 @@ bool setup_tables(TABLE_LIST *tables)
 }
 
 
-static key_map get_key_map_from_key_list(TABLE *table, 
-					 List<String> *index_list)
+key_map get_key_map_from_key_list(TABLE *table, 
+				  List<String> *index_list)
 {
   key_map map=0;
   List_iterator_fast<String> it(*index_list);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 925afde2202..996331b071f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -365,6 +365,7 @@ struct system_variables
   ulong net_retry_count;
   ulong net_wait_timeout;
   ulong net_write_timeout;
+  ulong preload_buff_size;
   ulong query_cache_type;
   ulong read_buff_size;
   ulong read_rnd_buff_size;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index f31b3305e07..04f784e62dc 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -59,7 +59,7 @@ enum enum_sql_command {
   SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,
   SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,
   SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
-  SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
+  SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_PRELOAD_KEYS,
   SQLCOM_FLUSH, SQLCOM_KILL,  SQLCOM_ANALYZE,
   SQLCOM_ROLLBACK, SQLCOM_COMMIT, SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
   SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1d2449839d8..2596d349a1c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1819,6 +1819,15 @@ mysql_execute_command(THD *thd)
     res = mysql_restore_table(thd, tables);
     break;
   }
+  case SQLCOM_PRELOAD_KEYS:
+  {
+    if (check_db_used(thd, tables) ||
+        check_access(thd, INDEX_ACL, tables->db, &tables->grant.privilege))
+      goto error; 
+    res = mysql_preload_keys(thd, tables);
+    break;
+  }
+      
 
 #ifndef EMBEDDED_LIBRARY
   case SQLCOM_CHANGE_MASTER:
@@ -3937,6 +3946,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
   ptr->lock_type=   lock_type;
   ptr->updating=    test(table_options & TL_OPTION_UPDATING);
   ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
+  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
   ptr->derived=	    table->sel;
   if (use_index)
     ptr->use_index=(List<String> *) thd->memdup((gptr) use_index,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 3cac88d05b2..a73f55077c9 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1331,6 +1331,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
 	goto err;
       continue;
     }
+    table->table->pos_in_table_list= table;
     if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
     {
       char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
@@ -1486,6 +1487,28 @@ int mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
 }
 
 
+/*
+  Preload specified indexes for a table into key cache
+
+  SYNOPSIS
+    mysql_preload_keys()
+    thd	        Thread object
+    tables      Table list (one table only)
+
+  RETURN VALUES
+    0	  ok
+   -1	  error
+*/
+
+int mysql_preload_keys(THD* thd, TABLE_LIST* tables)
+{
+  DBUG_ENTER("mysql_preload_keys");
+  DBUG_RETURN(mysql_admin_table(thd, tables, 0,
+				"preload_keys", TL_READ, 0, 0, 0,
+				&handler::preload_keys));
+}
+
+
 /*
   Create a table identical to the specified table
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e3b2c738949..6c120574644 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -256,6 +256,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token	KEY_SYM
 %token	LEADING
 %token	LEAST_SYM
+%token	LEAVES
 %token	LEVEL_SYM
 %token	LEX_HOSTNAME
 %token	LIKE
@@ -309,6 +310,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token	DUMPFILE
 %token	PACK_KEYS_SYM
 %token	PARTIAL
+%token	PRELOAD
 %token	PRIMARY_SYM
 %token	PRIVILEGES
 %token	PROCESS
@@ -584,7 +586,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 	type int_type real_type order_dir opt_field_spec lock_option
 	udf_type if_exists opt_local opt_table_options table_options
 	table_option opt_if_not_exists opt_no_write_to_binlog opt_var_type opt_var_ident_type
-	delete_option opt_temporary all_or_any opt_distinct
+	delete_option opt_temporary all_or_any opt_distinct opt_ignore_leafs
 
 %type <ulong_num>
 	ULONG_NUM raid_types merge_insert_types
@@ -613,7 +615,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 	key_alg opt_btree_or_rtree
 
 %type <string_list>
-	key_usage_list
+	key_usage_list 
 
 %type <key_part>
 	key_part
@@ -661,10 +663,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %type <NONE>
 	query verb_clause create change select do drop insert replace insert2
 	insert_values update delete truncate rename
-	show describe load alter optimize flush
+	show describe load alter optimize preload flush
 	reset purge begin commit rollback slave master_def master_defs
 	repair restore backup analyze check start
 	field_list field_list_item field_spec kill column_def key_def
+	preload_list preload_keys
 	select_item_list select_item values_list no_braces
 	opt_limit_clause delete_limit_clause fields opt_values values
 	procedure_list procedure_list2 procedure_item
@@ -733,6 +736,7 @@ verb_clause:
 	| lock
 	| kill
 	| optimize
+	| preload
 	| purge
 	| rename
         | repair
@@ -1824,6 +1828,55 @@ table_to_table:
 	    YYABORT;
 	};
 
+preload:
+	PRELOAD
+	{
+	  LEX *lex=Lex;
+	  lex->sql_command=SQLCOM_PRELOAD_KEYS;
+	}
+	preload_list
+	{}
+	;
+
+preload_list:
+	preload_keys
+	| preload_list ',' preload_keys;
+
+preload_keys:
+	table_ident preload_keys_spec opt_ignore_leafs
+	{
+	  LEX *lex=Lex;
+	  SELECT_LEX *sel= &lex->select_lex;
+	  if (!sel->add_table_to_list(lex->thd, $1, NULL, $3, 
+                                      TL_READ, 
+                                      sel->get_use_index(),
+                                      (List<String> *)0))
+            YYABORT;
+	}
+	;
+
+preload_keys_spec:
+	keys_or_index { Select->select_lex()->interval_list.empty(); }
+	preload_key_list_or_empty 
+	{
+	  LEX *lex=Lex;
+	  SELECT_LEX *sel= &lex->select_lex;
+	  sel->use_index= sel->interval_list;
+	  sel->use_index_ptr= &sel->use_index;
+	}          
+	;
+
+preload_key_list_or_empty:
+	/* empty */
+	| '(' key_usage_list2 ')' {}
+	;
+
+opt_ignore_leafs:
+	/* empty */
+	{ $$= 0; }
+	| IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; }
+	;
+
 /*
   Select : retrieve data from table
 */
@@ -4251,6 +4304,7 @@ keyword:
 	| INSERT_METHOD		{}
 	| RELAY_THREAD		{}
 	| LAST_SYM		{}
+	| LEAVES                {}
 	| LEVEL_SYM		{}
 	| LINESTRING		{}
 	| LOCAL_SYM		{}
@@ -4294,6 +4348,7 @@ keyword:
 	| PASSWORD		{}
 	| POINT_SYM		{}
 	| POLYGON		{}
+	| PRELOAD               {}
 	| PREV_SYM		{}
 	| PROCESS		{}
 	| PROCESSLIST_SYM	{}
diff --git a/sql/table.h b/sql/table.h
index 2aefe23cb2f..3132e72fb2f 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -181,6 +181,7 @@ typedef struct st_table_list
   bool		straight;		/* optimize with prev table */
   bool          updating;               /* for replicate-do/ignore table */
   bool		force_index;		/* Prefer index over table scan */
+  bool          ignore_leaves;          /* Preload only non-leaf nodes */
 } TABLE_LIST;
 
 typedef struct st_changed_table_list