diff -Narup ./019_mariadb-10.0.15-vp_ref/sql/ha_partition.cc ./020_mariadb-10.0.15-bulk_access/sql/ha_partition.cc
--- ./019_mariadb-10.0.15-vp_ref/sql/ha_partition.cc	2014-11-29 17:04:55.741801287 +0900
+++ ./020_mariadb-10.0.15-bulk_access/sql/ha_partition.cc	2014-11-29 17:05:04.673795477 +0900
@@ -242,6 +242,16 @@ const uint32 ha_partition::NO_CURRENT_PA
 
 ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share)
   :handler(hton, share)
+  , m_pre_calling(FALSE)
+  , m_pre_call_use_parallel(FALSE)
+#ifdef HA_CAN_BULK_ACCESS
+  , bulk_access_started(FALSE)
+  , bulk_access_executing(FALSE)
+  , bulk_access_pre_called(FALSE)
+  , bulk_access_info_first(NULL)
+  , bulk_access_info_current(NULL)
+  , bulk_access_info_exec_tgt(NULL)
+#endif
 {
   DBUG_ENTER("ha_partition::ha_partition(table)");
   ft_first = NULL;
@@ -265,6 +275,16 @@ ha_partition::ha_partition(handlerton *h
 
 ha_partition::ha_partition(handlerton *hton, partition_info *part_info)
   :handler(hton, NULL)
+  , m_pre_calling(FALSE)
+  , m_pre_call_use_parallel(FALSE)
+#ifdef HA_CAN_BULK_ACCESS
+  , bulk_access_started(FALSE)
+  , bulk_access_executing(FALSE)
+  , bulk_access_pre_called(FALSE)
+  , bulk_access_info_first(NULL)
+  , bulk_access_info_current(NULL)
+  , bulk_access_info_exec_tgt(NULL)
+#endif
 {
   DBUG_ENTER("ha_partition::ha_partition(part_info)");
   DBUG_ASSERT(part_info);
@@ -295,6 +315,16 @@ ha_partition::ha_partition(handlerton *h
                            ha_partition *clone_arg,
                            MEM_ROOT *clone_mem_root_arg)
   :handler(hton, share)
+  , m_pre_calling(FALSE)
+  , m_pre_call_use_parallel(FALSE)
+#ifdef HA_CAN_BULK_ACCESS
+  , bulk_access_started(FALSE)
+  , bulk_access_executing(FALSE)
+  , bulk_access_pre_called(FALSE)
+  , bulk_access_info_first(NULL)
+  , bulk_access_info_current(NULL)
+  , bulk_access_info_exec_tgt(NULL)
+#endif
 {
   DBUG_ENTER("ha_partition::ha_partition(clone)");
   ft_first = NULL;
@@ -3345,6 +3375,9 @@ void ha_partition::free_partition_bitmap
 {
   /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
   my_bitmap_free(&m_bulk_insert_started);
+#ifdef HA_CAN_BULK_ACCESS
+  my_bitmap_free(&bulk_access_exec_bitmap);
+#endif
   my_bitmap_free(&m_locked_partitions);
   my_bitmap_free(&m_partitions_to_reset);
   my_bitmap_free(&m_key_not_found_partitions);
@@ -3362,11 +3395,22 @@ bool ha_partition::init_partition_bitmap
   if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
     DBUG_RETURN(true);
   bitmap_clear_all(&m_bulk_insert_started);
+#ifdef HA_CAN_BULK_ACCESS
+  if (my_bitmap_init(&bulk_access_exec_bitmap, NULL, m_tot_parts, FALSE))
+  {
+    my_bitmap_free(&m_bulk_insert_started);
+    DBUG_RETURN(true);
+  }
+  bitmap_clear_all(&bulk_access_exec_bitmap);
+#endif
 
   /* Initialize the bitmap we use to keep track of locked partitions */
   if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE))
   {
     my_bitmap_free(&m_bulk_insert_started);
+#ifdef HA_CAN_BULK_ACCESS
+    my_bitmap_free(&bulk_access_exec_bitmap);
+#endif
     DBUG_RETURN(true);
   }
   bitmap_clear_all(&m_locked_partitions);
@@ -3378,6 +3422,9 @@ bool ha_partition::init_partition_bitmap
   if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE))
   {
     my_bitmap_free(&m_bulk_insert_started);
+#ifdef HA_CAN_BULK_ACCESS
+    my_bitmap_free(&bulk_access_exec_bitmap);
+#endif
     my_bitmap_free(&m_locked_partitions);
     DBUG_RETURN(true);
   }
@@ -3390,6 +3437,9 @@ bool ha_partition::init_partition_bitmap
   if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
   {
     my_bitmap_free(&m_bulk_insert_started);
+#ifdef HA_CAN_BULK_ACCESS
+    my_bitmap_free(&bulk_access_exec_bitmap);
+#endif
     my_bitmap_free(&m_locked_partitions);
     my_bitmap_free(&m_partitions_to_reset);
     DBUG_RETURN(true);
@@ -3736,6 +3786,16 @@ int ha_partition::close(void)
   DBUG_ENTER("ha_partition::close");
 
   DBUG_ASSERT(table->s == table_share);
+#ifdef HA_CAN_BULK_ACCESS
+  if (bulk_access_info_first)
+  {
+    do {
+      bulk_access_info_current = bulk_access_info_first->next;
+      delete_bulk_access_info(bulk_access_info_first);
+      bulk_access_info_first = bulk_access_info_current;
+    } while (bulk_access_info_first);
+  }
+#endif
   if (ft_first)
   {
     st_partition_ft_info *tmp_ft_info;
@@ -3832,7 +3892,7 @@ repeat:
 
 int ha_partition::external_lock(THD *thd, int lock_type)
 {
-  uint error;
+  int error;
   uint i, first_used_partition;
   MY_BITMAP *used_partitions;
   DBUG_ENTER("ha_partition::external_lock");
@@ -3894,6 +3954,24 @@ err_handler:
 }
 
 
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::additional_lock(THD *thd, enum thr_lock_type lock_type)
+{
+  int error;
+  handler **file;
+  DBUG_ENTER("ha_partition::additional_lock");
+  file= m_file;
+  do
+  {
+    DBUG_PRINT("info", ("store lock %d iteration", (int) (file - m_file)));
+    if ((error= (*file)->additional_lock(thd, lock_type)))
+      DBUG_RETURN(error);
+  } while (*(++file));
+  DBUG_RETURN(0);
+}
+#endif
+
+
 /*
   Get the lock(s) for the table and perform conversion of locks if needed
 
@@ -4193,6 +4271,10 @@ int ha_partition::write_row(uchar * buf)
   */
   if (have_auto_increment)
   {
+#ifdef HA_CAN_BULK_ACCESS
+    if (!bulk_access_executing || !bulk_access_info_exec_tgt->called)
+    {
+#endif
     if (!part_share->auto_inc_initialized &&
         !table_share->next_number_keypart)
     {
@@ -4211,6 +4293,9 @@ int ha_partition::write_row(uchar * buf)
     */
     if (error)
       goto exit;
+#ifdef HA_CAN_BULK_ACCESS
+    }
+#endif
 
     /*
       Don't allow generation of auto_increment value the partitions handler.
@@ -4260,6 +4345,108 @@ exit:
 }
 
 
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_write_row(uchar * buf)
+{
+  uint32 part_id;
+  int error;
+  longlong func_value;
+  bool have_auto_increment=
+    table->next_number_field && buf == table->record[0];
+  my_bitmap_map *old_map;
+  THD *thd= ha_thd();
+  ulong saved_sql_mode= thd->variables.sql_mode;
+  bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null;
+#ifdef NOT_NEEDED
+  uchar *rec0= m_rec0;
+#endif
+  DBUG_ENTER("ha_partition::pre_write_row");
+  DBUG_ASSERT(buf == m_rec0);
+
+  /* If we have a timestamp column, update it to the current time */
+
+  /*
+    If we have an auto_increment column and we are writing a changed row
+    or a new row, then update the auto_increment value in the record.
+  */
+  if (have_auto_increment)
+  {
+    if (!part_share->auto_inc_initialized &&
+        !table_share->next_number_keypart)
+    {
+      /*
+        If auto_increment in table_share is not initialized, start by
+        initializing it.
+      */
+      info(HA_STATUS_AUTO);
+    }
+    error= update_auto_increment();
+
+    /*
+      If we have failed to set the auto-increment value for this row,
+      it is highly likely that we will not be able to insert it into
+      the correct partition. We must check and fail if neccessary.
+    */
+    if (error)
+      goto exit;
+
+    /*
+      Don't allow generation of auto_increment value the partitions handler.
+      If a partitions handler would change the value, then it might not
+      match the partition any longer.
+      This can occur if 'SET INSERT_ID = 0; INSERT (NULL)',
+      So allow this by adding 'MODE_NO_AUTO_VALUE_ON_ZERO' to sql_mode.
+      The partitions handler::next_insert_id must always be 0. Otherwise
+      we need to forward release_auto_increment, or reset it for all
+      partitions.
+    */
+    if (table->next_number_field->val_int() == 0)
+    {
+      table->auto_increment_field_not_null= TRUE;
+      thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
+    }
+  }
+
+  old_map= dbug_tmp_use_all_columns(table, table->read_set);
+#ifdef NOT_NEEDED
+  if (likely(buf == rec0))
+#endif
+    error= m_part_info->get_partition_id(m_part_info, &part_id,
+                                         &func_value);
+#ifdef NOT_NEEDED
+  else
+  {
+    set_field_ptr(m_part_field_array, buf, rec0);
+    error= m_part_info->get_partition_id(m_part_info, &part_id,
+                                         &func_value);
+    set_field_ptr(m_part_field_array, rec0, buf);
+  }
+#endif
+  dbug_tmp_restore_column_map(table->read_set, old_map);
+  if (unlikely(error))
+  {
+    m_part_info->err_value= func_value;
+    goto exit;
+  }
+  m_last_part= part_id;
+  DBUG_PRINT("info", ("Insert in partition %d", part_id));
+  start_part_bulk_insert(thd, part_id);
+
+  tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+  error= m_file[part_id]->ha_pre_write_row(buf);
+  if (!error)
+    bitmap_set_bit(&bulk_access_exec_bitmap, part_id);
+  if (have_auto_increment && !table->s->next_number_keypart)
+    set_auto_increment_if_higher(table->next_number_field);
+  reenable_binlog(thd);
+exit:
+  thd->variables.sql_mode= saved_sql_mode;
+  table->auto_increment_field_not_null= saved_auto_inc_field_not_null;
+  DBUG_RETURN(error);
+}
+#endif
+
+
 /*
   Update an existing row
 
@@ -4284,6 +4471,7 @@ exit:
     old_data is always record[1]
 */
 
+
 int ha_partition::update_row(const uchar *old_data, uchar *new_data)
 {
   THD *thd= ha_thd();
@@ -4919,6 +5107,114 @@ err1:
 }
 
 
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_rnd_init(bool scan)
+{
+  int error;
+  uint i= 0;
+  uint32 part_id;
+  DBUG_ENTER("ha_partition::pre_rnd_init");
+
+  /*
+    For operations that may need to change data, we may need to extend
+    read_set.
+  */
+  if (m_lock_type == F_WRLCK)
+  {
+    /*
+      If write_set contains any of the fields used in partition and
+      subpartition expression, we need to set all bits in read_set because
+      the row may need to be inserted in a different [sub]partition. In
+      other words update_row() can be converted into write_row(), which
+      requires a complete record.
+    */
+    if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+                              table->write_set))
+      bitmap_set_all(table->read_set);
+    else
+    {
+      /*
+        Some handlers only read fields as specified by the bitmap for the
+        read set. For partitioned handlers we always require that the
+        fields of the partition functions are read such that we can
+        calculate the partition id to place updated and deleted records.
+      */
+      bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+    }
+  }
+
+  /* Now we see what the index of our first important partition is */
+  DBUG_PRINT("info", ("m_part_info->read_partitions: 0x%lx",
+                      (long) m_part_info->read_partitions.bitmap));
+  part_id= bitmap_get_first_set(&(m_part_info->read_partitions));
+  DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id));
+
+  if (MY_BIT_NONE == part_id)
+  {
+    error= 0;
+    goto err1;
+  }
+
+  /*
+    We have a partition and we are scanning with rnd_next
+    so we bump our cache
+  */
+  DBUG_PRINT("info", ("rnd_init on partition %d", part_id));
+  if (scan)
+  {
+    /*
+      rnd_end() is needed for partitioning to reset internal data if scan
+      is already in use
+    */
+    pre_rnd_end();
+    late_extra_cache(part_id);
+    m_index_scan_type = partition_no_index_scan;
+    for (i= part_id; i < m_tot_parts; i++)
+    {
+      if (bitmap_is_set(&(m_part_info->read_partitions), i))
+      {
+        if ((error= m_file[i]->ha_pre_rnd_init(scan)))
+          goto err2;
+        bitmap_set_bit(&bulk_access_exec_bitmap, i);
+      }
+    }
+  }
+  else
+  {
+    for (i= part_id; i < m_tot_parts; i++)
+    {
+      if (bitmap_is_set(&(m_part_info->read_partitions), i))
+      {
+        if ((error= m_file[i]->ha_pre_rnd_init(scan)))
+          goto err;
+        bitmap_set_bit(&bulk_access_exec_bitmap, i);
+      }
+    }
+  }
+  m_scan_value= scan;
+  m_part_spec.start_part= part_id;
+  m_part_spec.end_part= m_tot_parts - 1;
+  m_rnd_init_and_first = TRUE;
+  DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value));
+  DBUG_RETURN(0);
+
+err2:
+  late_extra_no_cache(part_id);
+err:
+  for (;
+       part_id < i;
+       part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id))
+  {
+    m_file[part_id]->ha_pre_rnd_end();
+  }
+err1:
+  m_scan_value= 2;
+  m_part_spec.start_part= NO_CURRENT_PART_ID;
+  DBUG_RETURN(error);
+}
+#endif
+
+
 /*
   End of a table scan
 
@@ -4967,6 +5263,44 @@ int ha_partition::rnd_end()
   DBUG_RETURN(0);
 }
 
+
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_rnd_end()
+{
+  handler **file;
+  DBUG_ENTER("ha_partition::pre_rnd_end");
+  switch (m_scan_value) {
+  case 2:                                       // Error
+    break;
+  case 1:
+    if (NO_CURRENT_PART_ID != m_part_spec.start_part)         // Table scan
+    {
+      late_extra_no_cache(m_part_spec.start_part);
+    }
+    file= m_file;
+    do
+    {
+      if (bitmap_is_set(&(m_part_info->read_partitions), (file - m_file)))
+        (*file)->ha_pre_rnd_end();
+    } while (*(++file));
+    break;
+  case 0:
+    uint i;
+    for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+         i < m_tot_parts;
+         i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+    {
+      m_file[i]->ha_pre_rnd_end();
+    }
+    break;
+  }
+  m_scan_value= 2;
+  m_part_spec.start_part= NO_CURRENT_PART_ID;
+  DBUG_RETURN(0);
+}
+#endif
+
+
 /*
   read next row during full table scan (scan in random row order)
 
@@ -5012,9 +5346,16 @@ int ha_partition::rnd_next(uchar *buf)
   if (m_rnd_init_and_first)
   {
     m_rnd_init_and_first = FALSE;
+#ifdef HA_CAN_BULK_ACCESS
+    if (!bulk_access_executing)
+    {
+#endif
     error = handle_pre_scan(FALSE, FALSE);
     if (error)
       DBUG_RETURN(error);
+#ifdef HA_CAN_BULK_ACCESS
+    }
+#endif
   }
 
   file= m_file[part_id];
@@ -5400,10 +5741,91 @@ err:
 }
 
 
-/*
-  End of index scan
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_index_init(uint inx, bool sorted)
+{
+  int error= 0;
+  uint i;
+  DBUG_ENTER("ha_partition::pre_index_init");
 
-  SYNOPSIS
+  DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted));
+  active_index= inx;
+  m_part_spec.start_part= NO_CURRENT_PART_ID;
+  m_start_key.length= 0;
+  m_ordered= sorted;
+  m_curr_key_info[0]= table->key_info+inx;
+  if (m_pkey_is_clustered && table->s->primary_key != MAX_KEY)
+  {
+    /*
+      if PK is clustered, then the key cmp must use the pk to
+      differentiate between equal key in given index.
+    */
+    DBUG_PRINT("info", ("Clustered pk, using pk as secondary cmp"));
+    m_curr_key_info[1]= table->key_info+table->s->primary_key;
+    m_curr_key_info[2]= NULL;
+  }
+  else
+    m_curr_key_info[1]= NULL;
+  /*
+    Some handlers only read fields as specified by the bitmap for the
+    read set. For partitioned handlers we always require that the
+    fields of the partition functions are read such that we can
+    calculate the partition id to place updated and deleted records.
+    But this is required for operations that may need to change data only.
+  */
+  if (m_lock_type == F_WRLCK)
+    bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+  if (sorted)
+  {
+    /*
+      An ordered scan is requested. We must make sure all fields of the 
+      used index are in the read set, as partitioning requires them for
+      sorting (see ha_partition::handle_ordered_index_scan).
+
+      The SQL layer may request an ordered index scan without having index
+      fields in the read set when
+       - it needs to do an ordered scan over an index prefix.
+       - it evaluates ORDER BY with SELECT COUNT(*) FROM t1.
+
+      TODO: handle COUNT(*) queries via unordered scan.
+    */
+    KEY **key_info= m_curr_key_info;
+    do
+    {
+      for (i= 0; i < (*key_info)->user_defined_key_parts; i++)
+        bitmap_set_bit(table->read_set,
+                       (*key_info)->key_part[i].field->field_index);
+    } while (*(++key_info));
+  }
+  for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+       i < m_tot_parts;
+       i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+  {
+    if ((error= m_file[i]->ha_pre_index_init(inx, sorted)))
+    {
+      goto error;
+    }
+    bitmap_set_bit(&bulk_access_exec_bitmap, i);
+  }
+  DBUG_RETURN(error);
+
+error:
+  uint j;
+  for (j= bitmap_get_first_set(&m_part_info->read_partitions);
+       j < i;
+       j= bitmap_get_next_set(&m_part_info->read_partitions, j))
+  {
+    (void) m_file[j]->ha_pre_index_end();
+  }
+  DBUG_RETURN(error);
+}
+#endif
+
+
+/*
+  End of index scan
+
+  SYNOPSIS
     index_end()
 
   RETURN VALUE
@@ -5441,6 +5863,119 @@ int ha_partition::index_end()
 }
 
 
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_index_end()
+{
+  int error= 0;
+  uint i;
+  DBUG_ENTER("ha_partition::pre_index_end");
+
+  active_index= MAX_KEY;
+  m_part_spec.start_part= NO_CURRENT_PART_ID;
+  for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+       i < m_tot_parts;
+       i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+  {
+    int tmp;
+    if ((tmp= m_file[i]->ha_pre_index_end()))
+      error= tmp;
+  }
+  DBUG_RETURN(error);
+}
+#endif
+
+
+int ha_partition::pre_index_read_map(const uchar *key,
+                                     key_part_map keypart_map,
+                                     enum ha_rkey_function find_flag,
+                                     bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_index_read_map");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = index_read_map(table->record[0], key, keypart_map, find_flag);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_index_first(bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_index_first");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = index_first(table->record[0]);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_index_last(bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_index_last");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = index_last(table->record[0]);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_multi_range_read_next(bool use_parallel)
+{
+  int error;
+  range_id_t range_info;
+  DBUG_ENTER("ha_partition::pre_multi_range_read_next");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = multi_range_read_next(&range_info);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_read_range_first(const key_range *start_key,
+                                       const key_range *end_key,
+                                       bool eq_range, bool sorted,
+                                       bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_read_range_first");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = read_range_first(start_key, end_key, eq_range, sorted);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_ft_read(bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_ft_read");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = ft_read(table->record[0]);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
+int ha_partition::pre_rnd_next(bool use_parallel)
+{
+  int error;
+  DBUG_ENTER("ha_partition::pre_rnd_next");
+  m_pre_calling= TRUE;
+  m_pre_call_use_parallel= use_parallel;
+  error = rnd_next(table->record[0]);
+  m_pre_calling= FALSE;
+  DBUG_RETURN(error);
+}
+
+
 /*
   Read one record in an index scan and start an index scan
 
@@ -5617,9 +6152,13 @@ int ha_partition::common_index_read(ucha
       The unordered index scan will use the partition set created.
     */
     DBUG_PRINT("info", ("doing unordered scan"));
-    error = handle_pre_scan(FALSE, FALSE);
-    if (!error)
-      error= handle_unordered_scan_next_partition(buf);
+    if (!bulk_access_executing)
+    {
+      error= handle_pre_scan(FALSE, FALSE);
+      if (m_pre_calling || error)
+        DBUG_RETURN(error);
+    }
+    error= handle_unordered_scan_next_partition(buf);
   }
   else
   {
@@ -5713,9 +6252,12 @@ int ha_partition::common_first_last(ucha
   if (!m_ordered_scan_ongoing &&
       m_index_scan_type != partition_index_last)
   {
-    error = handle_pre_scan(FALSE, FALSE);
-    if (error)
-      return error;
+    if (!bulk_access_executing)
+    {
+      error = handle_pre_scan(FALSE, FALSE);
+      if (m_pre_calling || error)
+        return error;
+    }
    return handle_unordered_scan_next_partition(buf);
   }
   return handle_ordered_index_scan(buf, FALSE);
@@ -6464,11 +7006,6 @@ int ha_partition::handle_pre_scan(bool r
     case partition_index_last:
       error= file->pre_index_last(use_parallel);
       break;
-    case partition_index_read_last:
-      error= file->pre_index_read_last_map(m_start_key.key,
-                                       m_start_key.keypart_map,
-                                       use_parallel);
-      break;
     case partition_read_range:
       error= file->pre_read_range_first(m_start_key.key? &m_start_key: NULL,
                                     end_range, eq_range, TRUE, use_parallel);
@@ -6493,6 +7030,8 @@ int ha_partition::handle_pre_scan(bool r
     if (error)
       DBUG_RETURN(error);
   }
+  if (bulk_access_started)
+    bulk_access_info_current->called = TRUE;
   table->status = 0;
   DBUG_RETURN(0);
 }
@@ -6737,9 +7276,13 @@ int ha_partition::handle_ordered_index_s
   int saved_error= HA_ERR_END_OF_FILE;
   DBUG_ENTER("ha_partition::handle_ordered_index_scan");
 
-  error = handle_pre_scan(reverse_order, TRUE);
-  if (error)
-    DBUG_RETURN(error);
+  if (!bulk_access_executing)
+  {
+    error = handle_pre_scan(reverse_order,
+                            (m_pre_calling ? m_pre_call_use_parallel : TRUE));
+    if (m_pre_calling || error)
+      DBUG_RETURN(error);
+  }
 
   if (m_key_not_found)
   {
@@ -7006,10 +7549,12 @@ int ha_partition::handle_ordered_index_s
 int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
 {
   int error;
+  DBUG_ENTER("ha_partition::handle_ordered_next");
+  if (m_top_entry == NO_CURRENT_PART_ID)
+    DBUG_RETURN(HA_ERR_END_OF_FILE);
   uint part_id= m_top_entry;
   uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
   handler *file;
-  DBUG_ENTER("ha_partition::handle_ordered_next");
   
   if (m_key_not_found)
   {
@@ -7192,10 +7737,12 @@ int ha_partition::handle_ordered_next(uc
 int ha_partition::handle_ordered_prev(uchar *buf)
 {
   int error;
+  DBUG_ENTER("ha_partition::handle_ordered_prev");
+  if (m_top_entry == NO_CURRENT_PART_ID)
+    DBUG_RETURN(HA_ERR_END_OF_FILE);
   uint part_id= m_top_entry;
   uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
   handler *file= m_file[part_id];
-  DBUG_ENTER("ha_partition::handle_ordered_prev");
 
   if ((error= file->ha_index_prev(rec_buf)))
   {
@@ -8023,6 +8570,9 @@ int ha_partition::extra(enum ha_extra_fu
     m_num_locks = 0;
     additional_table_flags =
       (HA_HAS_RECORDS |
+#ifdef HA_CAN_BULK_ACCESS
+        HA_CAN_BULK_ACCESS |
+#endif
         HA_CAN_BG_SEARCH | HA_CAN_BG_INSERT | HA_CAN_BG_UPDATE);
     ref_length = 0;
     file = m_file;
@@ -8031,6 +8581,9 @@ int ha_partition::extra(enum ha_extra_fu
       m_num_locks += (*file)->lock_count();
       additional_table_flags &= ~((ulonglong) ((*file)->ha_table_flags() ^
         (HA_HAS_RECORDS |
+#ifdef HA_CAN_BULK_ACCESS
+          HA_CAN_BULK_ACCESS |
+#endif
           HA_CAN_BG_SEARCH | HA_CAN_BG_INSERT | HA_CAN_BG_UPDATE)));
       set_if_bigger(ref_length, ((*file)->ref_length));
     } while (*(++file));
@@ -8055,6 +8608,9 @@ int ha_partition::extra(enum ha_extra_fu
   case HA_EXTRA_DETACH_CHILDREN:
     cached_table_flags &= ~((ulonglong)
       (HA_HAS_RECORDS |
+#ifdef HA_CAN_BULK_ACCESS
+        HA_CAN_BULK_ACCESS |
+#endif
         HA_CAN_BG_SEARCH | HA_CAN_BG_INSERT | HA_CAN_BG_UPDATE));
     DBUG_RETURN(loop_extra(operation));
   /*
@@ -8104,6 +8660,25 @@ int ha_partition::reset(void)
   }
   bitmap_clear_all(&m_partitions_to_reset);
   ft_current = NULL;
+#ifdef HA_CAN_BULK_ACCESS
+  if (bulk_access_info_first)
+  {
+    PARTITION_BULK_ACCESS_INFO *bulk_access_info = bulk_access_info_first;
+    while (
+      bulk_access_info &&
+      bulk_access_info->used
+    ) {
+      bulk_access_info->used = FALSE;
+      bulk_access_info = bulk_access_info->next;
+    }
+    bitmap_clear_all(&bulk_access_exec_bitmap);
+  }
+  bulk_access_started = FALSE;
+  bulk_access_executing = FALSE;
+  bulk_access_pre_called = FALSE;
+  bulk_access_info_current = NULL;
+  bulk_access_info_exec_tgt = NULL;
+#endif
   DBUG_RETURN(result);
 }
 
@@ -10150,6 +10725,82 @@ int ha_partition::direct_update_rows_ini
   DBUG_RETURN(0);
 }
 
+
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_direct_update_rows_init(uint mode,
+  KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, uchar *new_data)
+{
+  int error;
+  uint i, j;
+  handler *file;
+  TABLE_LIST *table_list= table->pos_in_table_list;
+  DBUG_ENTER("ha_partition::pre_direct_update_rows_init");
+  if (
+    bitmap_is_overlapping(&m_part_info->full_part_field_set,
+                          table->write_set)
+#if defined(HS_HAS_SQLCOM)
+    && (
+      thd_sql_command(ha_thd()) != SQLCOM_HS_UPDATE ||
+      check_hs_update_overlapping(&ranges->start_key)
+    )
+#endif
+  ) {
+    DBUG_PRINT("info",("partition FALSE by updating part_key"));
+    DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+  }
+
+  if (ranges && ranges->start_key.key)
+    get_partition_set(table, table->record[1], active_index,
+      &ranges->start_key, &m_part_spec);
+  else {
+    m_part_spec.start_part= 0;
+    m_part_spec.end_part= m_tot_parts - 1;
+  }
+  m_direct_update_part_spec = m_part_spec;
+
+  j = 0;
+  for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+  {
+    if (
+      bitmap_is_set(&(m_part_info->read_partitions), i) &&
+      bitmap_is_set(&(m_part_info->lock_partitions), i)
+    ) {
+      file = m_file[i];
+      if ((error= file->ha_pre_direct_update_rows_init(mode, ranges,
+        range_count, sorted, new_data)))
+      {
+        DBUG_PRINT("info",("partition FALSE by storage engine"));
+        DBUG_RETURN(error);
+      }
+      j++;
+    }
+  }
+
+  if (j != 1 && table_list)
+  {
+    while (table_list->parent_l)
+      table_list = table_list->parent_l;
+    st_select_lex *select_lex= table_list->select_lex;
+    DBUG_PRINT("info",("partition select_lex=%p", select_lex));
+    if (select_lex && select_lex->explicit_limit)
+    {
+      DBUG_PRINT("info",("partition explicit_limit=TRUE"));
+      DBUG_PRINT("info",("partition offset_limit=%p",
+        select_lex->offset_limit));
+      DBUG_PRINT("info",("partition select_limit=%p",
+        select_lex->select_limit));
+      DBUG_PRINT("info",("partition FALSE by select_lex"));
+      DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+    }
+  }
+  if (bulk_access_started)
+    bulk_access_info_current->called = TRUE;
+  DBUG_PRINT("info",("partition OK"));
+  DBUG_RETURN(0);
+}
+#endif
+
+
 int ha_partition::direct_update_rows(KEY_MULTI_RANGE *ranges, uint range_count,
   bool sorted, uchar *new_data, uint *update_rows)
 {
@@ -10193,6 +10844,52 @@ int ha_partition::direct_update_rows(KEY
   DBUG_RETURN(0);
 }
 
+
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_direct_update_rows(KEY_MULTI_RANGE *ranges,
+  uint range_count, bool sorted, uchar *new_data, uint *update_rows)
+{
+  int error;
+  bool rnd_seq= FALSE;
+  uint m_update_rows, i;
+  handler *file;
+  DBUG_ENTER("ha_partition::pre_direct_update_rows");
+
+  if (pre_inited == RND && m_scan_value == 1)
+  {
+    rnd_seq= TRUE;
+    m_scan_value= 2;
+  }
+  m_part_spec = m_direct_update_part_spec;
+  for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+  {
+    file = m_file[i];
+    if (
+      bitmap_is_set(&(m_part_info->read_partitions), i) &&
+      bitmap_is_set(&(m_part_info->lock_partitions), i)
+    ) {
+      if (rnd_seq && file->pre_inited == NONE)
+      {
+        if ((error = file->ha_pre_rnd_init(TRUE)))
+          DBUG_RETURN(error);
+      }
+      if ((error= (file)->ha_pre_direct_update_rows(ranges, range_count,
+        sorted, new_data, &m_update_rows)))
+      {
+        if (rnd_seq)
+          file->ha_pre_rnd_end();
+        DBUG_RETURN(error);
+      }
+      bitmap_set_bit(&bulk_access_exec_bitmap, i);
+    }
+    if (rnd_seq && (error = file->ha_pre_index_or_rnd_end()))
+      DBUG_RETURN(error);
+  }
+  DBUG_RETURN(0);
+}
+#endif
+
+
 #if defined(HS_HAS_SQLCOM)
 bool ha_partition::check_hs_update_overlapping(key_range *key)
 {
@@ -10322,6 +11019,68 @@ int ha_partition::direct_delete_rows_ini
   DBUG_RETURN(0);
 }
 
+
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_direct_delete_rows_init(uint mode,
+  KEY_MULTI_RANGE *ranges, uint range_count, bool sorted)
+{
+  int error;
+  uint i, j;
+  handler *file;
+  TABLE_LIST *table_list= table->pos_in_table_list;
+  DBUG_ENTER("ha_partition::pre_direct_delete_rows_init");
+  if (ranges && ranges->start_key.key)
+    get_partition_set(table, table->record[0], active_index,
+      &ranges->start_key, &m_part_spec);
+  else {
+    m_part_spec.start_part= 0;
+    m_part_spec.end_part= m_tot_parts - 1;
+  }
+  m_direct_update_part_spec = m_part_spec;
+
+  j = 0;
+  for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+  {
+    if (
+      bitmap_is_set(&(m_part_info->read_partitions), i) &&
+      bitmap_is_set(&(m_part_info->lock_partitions), i)
+    ) {
+      file = m_file[i];
+      if ((error= file->ha_pre_direct_delete_rows_init(mode, ranges,
+        range_count, sorted)))
+      {
+        DBUG_PRINT("info",("partition FALSE by storage engine"));
+        DBUG_RETURN(error);
+      }
+      j++;
+    }
+  }
+
+  if (j != 1 && table_list)
+  {
+    while (table_list->parent_l)
+      table_list = table_list->parent_l;
+    st_select_lex *select_lex= table_list->select_lex;
+    DBUG_PRINT("info",("partition select_lex=%p", select_lex));
+    if (select_lex && select_lex->explicit_limit)
+    {
+      DBUG_PRINT("info",("partition explicit_limit=TRUE"));
+      DBUG_PRINT("info",("partition offset_limit=%p",
+        select_lex->offset_limit));
+      DBUG_PRINT("info",("partition select_limit=%p",
+        select_lex->select_limit));
+      DBUG_PRINT("info",("partition FALSE by select_lex"));
+      DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+    }
+  }
+  if (bulk_access_started)
+    bulk_access_info_current->called = TRUE;
+  DBUG_PRINT("info",("partition OK"));
+  DBUG_RETURN(0);
+}
+#endif
+
+
 int ha_partition::direct_delete_rows(KEY_MULTI_RANGE *ranges, uint range_count,
   bool sorted, uint *delete_rows)
 {
@@ -10365,12 +11124,122 @@ int ha_partition::direct_delete_rows(KEY
   DBUG_RETURN(0);
 }
 
+
+#ifdef HA_CAN_BULK_ACCESS
+int ha_partition::pre_direct_delete_rows(KEY_MULTI_RANGE *ranges,
+  uint range_count, bool sorted, uint *delete_rows)
+{
+  int error;
+  bool rnd_seq= FALSE;
+  uint m_delete_rows, i;
+  handler *file;
+  DBUG_ENTER("ha_partition::pre_direct_delete_rows");
+
+  if (pre_inited == RND && m_scan_value == 1)
+  {
+    rnd_seq= TRUE;
+    m_scan_value= 2;
+  }
+  m_part_spec = m_direct_update_part_spec;
+  for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+  {
+    file = m_file[i];
+    if (
+      bitmap_is_set(&(m_part_info->read_partitions), i) &&
+      bitmap_is_set(&(m_part_info->lock_partitions), i)
+    ) {
+      if (rnd_seq && file->pre_inited == NONE)
+      {
+        if ((error = file->ha_pre_rnd_init(TRUE)))
+          DBUG_RETURN(error);
+      }
+      if ((error= file->ha_pre_direct_delete_rows(ranges, range_count,
+        sorted, &m_delete_rows)))
+      {
+        if (rnd_seq)
+          file->ha_pre_rnd_end();
+        DBUG_RETURN(error);
+      }
+      bitmap_set_bit(&bulk_access_exec_bitmap, i);
+    }
+    if (rnd_seq && (error = file->ha_pre_index_or_rnd_end()))
+      DBUG_RETURN(error);
+  }
+  DBUG_RETURN(0);
+}
+#endif
+
+
 int ha_partition::info_push(uint info_type, void *info)
 {
   int error= 0;
+  uint i;
   handler **file= m_file;
   DBUG_ENTER("ha_partition::info_push");
 
+#ifdef HA_CAN_BULK_ACCESS
+  switch (info_type)
+  {
+    case INFO_KIND_BULK_ACCESS_BEGIN:
+      DBUG_PRINT("info",("partition INFO_KIND_BULK_ACCESS_BEGIN"));
+      if (bulk_access_started)
+      {
+        if (!bulk_access_info_current->next)
+        {
+          if (!(bulk_access_info_current->next = create_bulk_access_info()))
+          {
+            DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+          }
+          bulk_access_info_current->next->sequence_num =
+            bulk_access_info_current->sequence_num + 1;
+        }
+        bulk_access_info_current = bulk_access_info_current->next;
+      } else {
+        if (!bulk_access_info_first)
+        {
+          if (!(bulk_access_info_first = create_bulk_access_info()))
+          {
+            DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+          }
+          bulk_access_info_first->sequence_num = 0;
+        }
+        bulk_access_info_current = bulk_access_info_first;
+        bulk_access_started = TRUE;
+        bulk_access_executing = FALSE;
+      }
+      bulk_access_info_current->used = TRUE;
+      bulk_access_info_current->called = FALSE;
+      *((void **) info) = bulk_access_info_current;
+      i = 0;
+      do
+      {
+        int tmp;
+        if ((tmp = (*file)->info_push(info_type,
+          &bulk_access_info_current->info[i])))
+          error= tmp;
+        ++i;
+      } while (*(++file));
+      DBUG_RETURN(error);
+    case INFO_KIND_BULK_ACCESS_CURRENT:
+      DBUG_PRINT("info",("partition INFO_KIND_BULK_ACCESS_CURRENT"));
+      bulk_access_executing = TRUE;
+      bulk_access_info_exec_tgt = (PARTITION_BULK_ACCESS_INFO *) info;
+      i = 0;
+      do
+      {
+        int tmp;
+        if ((tmp = (*file)->info_push(info_type,
+          bulk_access_info_exec_tgt->info[i])))
+          error= tmp;
+        ++i;
+      } while (*(++file));
+      DBUG_RETURN(error);
+    case INFO_KIND_BULK_ACCESS_END:
+      DBUG_PRINT("info",("partition INFO_KIND_BULK_ACCESS_END"));
+      bulk_access_started = FALSE;
+      break;
+  }
+#endif
   do
   {
     int tmp;
@@ -10379,6 +11248,24 @@ int ha_partition::info_push(uint info_ty
   } while (*(++file));
   DBUG_RETURN(error);
 }
+
+
+void ha_partition::bulk_req_exec()
+{
+  uint i;
+  handler **file;
+  DBUG_ENTER("ha_partition::bulk_req_exec");
+  DBUG_PRINT("info",("partition this=%p", this));
+  for (file= m_file, i= 0; *file; ++file, ++i)
+  {
+    if (bitmap_is_set(&bulk_access_exec_bitmap, i))
+    {
+      (*file)->bulk_req_exec();
+    }
+  }
+  bitmap_clear_all(&bulk_access_exec_bitmap);
+  DBUG_VOID_RETURN;
+}
 #endif
 
 
@@ -10396,6 +11283,42 @@ void ha_partition::clear_top_table_field
 #endif
 
 
+#ifdef HA_CAN_BULK_ACCESS
+PARTITION_BULK_ACCESS_INFO *ha_partition::create_bulk_access_info()
+{
+  PARTITION_BULK_ACCESS_INFO *bulk_access_info;
+  void **tmp_info;
+  DBUG_ENTER("ha_partition::create_bulk_access_info");
+  DBUG_PRINT("info",("partition this=%p", this));
+  if (!(bulk_access_info = (PARTITION_BULK_ACCESS_INFO *)
+    my_multi_malloc(MYF(MY_WME),
+    &bulk_access_info, sizeof(PARTITION_BULK_ACCESS_INFO),
+    &tmp_info, sizeof(void *) * m_tot_parts,
+    NullS))
+  ) {
+    goto error_bulk_malloc;
+  }
+  bulk_access_info->info = tmp_info;
+  bulk_access_info->next = NULL;
+  bulk_access_info->used = FALSE;
+  DBUG_RETURN(bulk_access_info);
+
+error_bulk_malloc:
+  DBUG_RETURN(NULL);
+}
+
+
+void ha_partition::delete_bulk_access_info(
+  PARTITION_BULK_ACCESS_INFO *bulk_access_info
+) {
+  DBUG_ENTER("ha_partition::delete_bulk_access_info");
+  DBUG_PRINT("info",("partition this=%p", this));
+  my_free(bulk_access_info);
+  DBUG_VOID_RETURN;
+}
+#endif
+
+
 struct st_mysql_storage_engine partition_storage_engine=
 { MYSQL_HANDLERTON_INTERFACE_VERSION };
 
diff -Narup ./019_mariadb-10.0.15-vp_ref/sql/ha_partition.h ./020_mariadb-10.0.15-bulk_access/sql/ha_partition.h
--- ./019_mariadb-10.0.15-vp_ref/sql/ha_partition.h	2014-11-29 17:04:55.741801287 +0900
+++ ./020_mariadb-10.0.15-bulk_access/sql/ha_partition.h	2014-11-29 17:05:04.673795477 +0900
@@ -43,6 +43,18 @@ struct st_partition_ft_info
 };
 
 
+#ifdef HA_CAN_BULK_ACCESS
+typedef struct st_partition_bulk_access_info
+{
+  uint                          sequence_num;
+  bool                          used;
+  bool                          called;
+  void                          **info;
+  st_partition_bulk_access_info *next;
+} PARTITION_BULK_ACCESS_INFO;
+#endif
+
+
 /** Struct used for partition_name_hash */
 typedef struct st_part_name_def
 {
@@ -284,6 +296,17 @@ private:
 #ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS
   part_id_range m_direct_update_part_spec;
 #endif
+  bool                m_pre_calling;
+  bool                m_pre_call_use_parallel;
+#ifdef HA_CAN_BULK_ACCESS
+  bool                bulk_access_started;
+  bool                bulk_access_executing;
+  bool                bulk_access_pre_called;
+  PARTITION_BULK_ACCESS_INFO *bulk_access_info_first;
+  PARTITION_BULK_ACCESS_INFO *bulk_access_info_current;
+  PARTITION_BULK_ACCESS_INFO *bulk_access_info_exec_tgt;
+  MY_BITMAP           bulk_access_exec_bitmap;
+#endif
   /** keep track of locked partitions */
   MY_BITMAP m_locked_partitions;
   /** Stores shared auto_increment etc. */
@@ -454,6 +477,9 @@ public:
     currently InnoDB, BDB and NDB).
     -------------------------------------------------------------------------
   */
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int additional_lock(THD *thd, enum thr_lock_type lock_type);
+#endif
   virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
 				     enum thr_lock_type lock_type);
   virtual int external_lock(THD * thd, int lock_type);
@@ -512,6 +538,9 @@ public:
     number of calls to write_row.
   */
   virtual int write_row(uchar * buf);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_write_row(uchar *buf);
+#endif
   virtual bool start_bulk_update();
   virtual int exec_bulk_update(uint *dup_key_found);
   virtual void end_bulk_update();
@@ -520,8 +549,16 @@ public:
 #ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS
   virtual int direct_update_rows_init(uint mode, KEY_MULTI_RANGE *ranges,
     uint range_count, bool sorted, uchar *new_data);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_direct_update_rows_init(uint mode, KEY_MULTI_RANGE *ranges,
+    uint range_count, bool sorted, uchar *new_data);
+#endif
   virtual int direct_update_rows(KEY_MULTI_RANGE *ranges, uint range_count,
     bool sorted, uchar *new_data, uint *update_rows);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_direct_update_rows(KEY_MULTI_RANGE *ranges, uint range_count,
+    bool sorted, uchar *new_data, uint *update_rows);
+#endif
 #if defined(HS_HAS_SQLCOM)
   virtual bool check_hs_update_overlapping(key_range *key);
 #endif
@@ -532,8 +569,16 @@ public:
 #ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS
   virtual int direct_delete_rows_init(uint mode, KEY_MULTI_RANGE *ranges,
     uint range_count, bool sorted);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_direct_delete_rows_init(uint mode, KEY_MULTI_RANGE *ranges,
+    uint range_count, bool sorted);
+#endif
   virtual int direct_delete_rows(KEY_MULTI_RANGE *ranges, uint range_count,
     bool sorted, uint *delete_rows);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_direct_delete_rows(KEY_MULTI_RANGE *ranges, uint range_count,
+    bool sorted, uint *delete_rows);
+#endif
 #endif
   virtual int delete_all_rows(void);
   virtual int truncate();
@@ -590,7 +635,13 @@ public:
     and allocate it again
   */
   virtual int rnd_init(bool scan);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_rnd_init(bool scan);
+#endif
   virtual int rnd_end();
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_rnd_end();
+#endif
   virtual int rnd_next(uchar * buf);
   virtual int rnd_pos(uchar * buf, uchar * pos);
   virtual int rnd_pos_by_record(uchar *record);
@@ -628,11 +679,31 @@ public:
     index_init initializes an index before using it and index_end does
     any end processing needed.
   */
+  virtual int pre_index_read_map(const uchar *key,
+                                 key_part_map keypart_map,
+                                 enum ha_rkey_function find_flag,
+                                 bool use_parallel);
+  virtual int pre_index_first(bool use_parallel);
+  virtual int pre_index_last(bool use_parallel);
+  virtual int pre_multi_range_read_next(bool use_parallel);
+  virtual int pre_read_range_first(const key_range *start_key,
+                                   const key_range *end_key,
+                                   bool eq_range, bool sorted,
+                                   bool use_parallel);
+  virtual int pre_ft_read(bool use_parallel);
+  virtual int pre_rnd_next(bool use_parallel);
+
   virtual int index_read_map(uchar * buf, const uchar * key,
                              key_part_map keypart_map,
                              enum ha_rkey_function find_flag);
   virtual int index_init(uint idx, bool sorted);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_index_init(uint idx, bool sorted);
+#endif
   virtual int index_end();
+#ifdef HA_CAN_BULK_ACCESS
+  virtual int pre_index_end();
+#endif
 
   /**
     @breif
@@ -799,6 +870,9 @@ public:
     The next method will never be called if you do not implement indexes.
   */
   virtual double read_time(uint index, uint ranges, ha_rows rows);
+#ifdef HA_CAN_BULK_ACCESS
+  virtual void bulk_req_exec();
+#endif
   /*
     For the given range how many records are estimated to be in this range.
     Used by optimiser to calculate cost of using a particular index.
@@ -1377,6 +1451,12 @@ public:
     return h;
   }
 
+#ifdef HA_CAN_BULK_ACCESS
+  virtual PARTITION_BULK_ACCESS_INFO *create_bulk_access_info();
+  virtual void delete_bulk_access_info(
+    PARTITION_BULK_ACCESS_INFO *bulk_access_info);
+#endif
+
 
   friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
 };
diff -Narup ./019_mariadb-10.0.15-vp_ref/sql/handler.h ./020_mariadb-10.0.15-bulk_access/sql/handler.h
--- ./019_mariadb-10.0.15-vp_ref/sql/handler.h	2014-11-29 17:03:56.234796040 +0900
+++ ./020_mariadb-10.0.15-bulk_access/sql/handler.h	2014-11-29 17:05:04.680795203 +0900
@@ -45,6 +45,9 @@
 #define INFO_KIND_UPDATE_VALUES 102
 #define INFO_KIND_FORCE_LIMIT_BEGIN 103
 #define INFO_KIND_FORCE_LIMIT_END 104
+#define INFO_KIND_BULK_ACCESS_BEGIN 105
+#define INFO_KIND_BULK_ACCESS_CURRENT 106
+#define INFO_KIND_BULK_ACCESS_END 107
 
 #if MAX_KEY > 128
 #error MAX_KEY is too large.  Values up to 128 are supported.
@@ -274,6 +277,7 @@ enum enum_alter_inplace_result {
 #define HA_CAN_BG_INSERT       (1LL << 46)
 #define HA_CAN_BG_UPDATE       (1LL << 47)
 #define HA_CAN_MULTISTEP_MERGE (1LL << 48)
+#define HA_CAN_BULK_ACCESS     (1LL << 49)
 
 /* bits in index_flags(index_number) for what you can do with index */
 #define HA_READ_NEXT            1       /* TODO really use this flag */
@@ -2485,7 +2489,9 @@ public:
   /** Length of ref (1-8 or the clustered key length) */
   uint ref_length;
   FT_INFO *ft_handler;
-  enum {NONE=0, INDEX, RND} inited;
+  enum init_stat {NONE=0, INDEX, RND};
+  init_stat inited;
+  init_stat pre_inited;
   bool implicit_emptied;                /* Can be !=0 only if HEAP */
   const COND *pushed_cond;
   /**
@@ -2571,7 +2577,7 @@ public:
     ref(0), end_range(NULL), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
     in_range_check_pushed_down(FALSE),
     ref_length(sizeof(my_off_t)),
-    ft_handler(0), inited(NONE),
+    ft_handler(0), inited(NONE), pre_inited(NONE),
     implicit_emptied(0),
     pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
     pushed_idx_cond(NULL),
@@ -2590,6 +2596,7 @@ public:
   {
     DBUG_ASSERT(m_lock_type == F_UNLCK);
     DBUG_ASSERT(inited == NONE);
+    DBUG_ASSERT(pre_inited == NONE);
   }
   virtual handler *clone(const char *name, MEM_ROOT *mem_root);
   /** This is called after create to allow us to set up cached variables */
@@ -2614,6 +2621,15 @@ public:
     }
     DBUG_RETURN(result);
   }
+  int ha_pre_index_init(uint idx, bool sorted)
+  {
+    int result;
+    DBUG_ENTER("ha_pre_index_init");
+    DBUG_ASSERT(pre_inited==NONE);
+    if (!(result= pre_index_init(idx, sorted)))
+      pre_inited=INDEX;
+    DBUG_RETURN(result);
+  }
   int ha_index_end()
   {
     DBUG_ENTER("ha_index_end");
@@ -2623,6 +2639,13 @@ public:
     end_range=    NULL;
     DBUG_RETURN(index_end());
   }
+  int ha_pre_index_end()
+  {
+    DBUG_ENTER("ha_pre_index_end");
+    DBUG_ASSERT(pre_inited==INDEX);
+    pre_inited=NONE;
+    DBUG_RETURN(pre_index_end());
+  }
   /* This is called after index_init() if we need to do a index scan */
   virtual int prepare_index_scan() { return 0; }
   virtual int prepare_index_key_scan_map(const uchar * key, key_part_map keypart_map)
@@ -2645,6 +2668,14 @@ public:
     end_range= NULL;
     DBUG_RETURN(result);
   }
+  int ha_pre_rnd_init(bool scan)
+  {
+    int result;
+    DBUG_ENTER("ha_pre_rnd_init");
+    DBUG_ASSERT(pre_inited==NONE || (pre_inited==RND && scan));
+    pre_inited= (result= pre_rnd_init(scan)) ? NONE: RND;
+    DBUG_RETURN(result);
+  }
   int ha_rnd_end()
   {
     DBUG_ENTER("ha_rnd_end");
@@ -2653,6 +2684,13 @@ public:
     end_range= NULL;
     DBUG_RETURN(rnd_end());
   }
+  int ha_pre_rnd_end()
+  {
+    DBUG_ENTER("ha_pre_rnd_end");
+    DBUG_ASSERT(pre_inited==RND);
+    pre_inited=NONE;
+    DBUG_RETURN(pre_rnd_end());
+  }
   int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result));
   int ha_reset();
   /* this is necessary in many places, e.g. in HANDLER command */
@@ -2660,6 +2698,11 @@ public:
   {
     return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0;
   }
+  int ha_pre_index_or_rnd_end()
+  {
+    return pre_inited == INDEX ? ha_pre_index_end() : pre_inited == RND ?
+      ha_pre_rnd_end() : 0;
+  }
   /**
     The cached_table_flags is set at ha_open and ha_external_lock
   */
@@ -2672,6 +2715,7 @@ public:
   */
   int ha_external_lock(THD *thd, int lock_type);
   int ha_write_row(uchar * buf);
+  int ha_pre_write_row(uchar * buf) { return pre_write_row(buf); }
   int ha_update_row(const uchar * old_data, uchar * new_data);
   int ha_delete_row(const uchar * buf);
   int ha_direct_update_rows_init(
@@ -2681,6 +2725,14 @@ public:
     bool sorted,
     uchar *new_data
   );
+  int ha_pre_direct_update_rows_init(
+    uint mode,
+    KEY_MULTI_RANGE *ranges,
+    uint range_count,
+    bool sorted,
+    uchar *new_data
+  ) { return pre_direct_update_rows_init(mode, ranges, range_count, sorted,
+    new_data); }
   int ha_direct_update_rows(
     KEY_MULTI_RANGE *ranges,
     uint range_count,
@@ -2688,6 +2740,14 @@ public:
     uchar *new_data,
     uint *update_rows
   );
+  int ha_pre_direct_update_rows(
+    KEY_MULTI_RANGE *ranges,
+    uint range_count,
+    bool sorted,
+    uchar *new_data,
+    uint *update_rows
+  ) { return pre_direct_update_rows(ranges, range_count, sorted,
+    new_data, update_rows); }
   int ha_direct_update_row_binlog(
     const uchar *old_data,
     uchar *new_data
@@ -2698,15 +2758,29 @@ public:
     uint range_count,
     bool sorted
   );
+  int ha_pre_direct_delete_rows_init(
+    uint mode,
+    KEY_MULTI_RANGE *ranges,
+    uint range_count,
+    bool sorted
+  ) { return pre_direct_delete_rows_init(mode, ranges, range_count, sorted); }
   int ha_direct_delete_rows(
     KEY_MULTI_RANGE *ranges,
     uint range_count,
     bool sorted,
     uint *delete_rows
   );
+  int ha_pre_direct_delete_rows(
+    KEY_MULTI_RANGE *ranges,
+    uint range_count,
+    bool sorted,
+    uint *delete_rows
+  ) { return pre_direct_delete_rows(ranges, range_count, sorted,
+    delete_rows); }
   int ha_direct_delete_row_binlog(
     const uchar *buf
   );
+  virtual void bulk_req_exec() {}
   void ha_release_auto_increment();
 
   int check_collation_compatibility();
@@ -2938,17 +3012,6 @@ public:
    { return 0; }
   virtual int pre_index_last(bool use_parallel)
    { return 0; }
-  virtual int pre_index_read_last_map(const uchar *key,
-                                      key_part_map keypart_map,
-                                      bool use_parallel)
-   { return 0; }
-/*
-  virtual int pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
-                                         KEY_MULTI_RANGE *ranges,
-                                         uint range_count,
-                                         bool sorted, HANDLER_BUFFER *buffer,
-                                         bool use_parallel);
-*/
   virtual int pre_multi_range_read_next(bool use_parallel);
   virtual int pre_read_range_first(const key_range *start_key,
                                    const key_range *end_key,
@@ -3306,6 +3369,8 @@ public:
   virtual THR_LOCK_DATA **store_lock(THD *thd,
 				     THR_LOCK_DATA **to,
 				     enum thr_lock_type lock_type)=0;
+  virtual int additional_lock(THD *thd, enum thr_lock_type lock_type)
+  { return 0; }
 
   /** Type of table for caching query */
   virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; }
@@ -3854,7 +3919,9 @@ private:
   virtual int open(const char *name, int mode, uint test_if_locked)=0;
   /* Note: ha_index_read_idx_map() may bypass index_init() */
   virtual int index_init(uint idx, bool sorted) { return 0; }
+  virtual int pre_index_init(uint idx, bool sorted) { return 0; }
   virtual int index_end() { return 0; }
+  virtual int pre_index_end() { return 0; }
   /**
     rnd_init() can be called two times without rnd_end() in between
     (it only makes sense if scan=1).
@@ -3863,11 +3930,17 @@ private:
     to the start of the table, no need to deallocate and allocate it again
   */
   virtual int rnd_init(bool scan)= 0;
+  virtual int pre_rnd_init(bool scan) { return 0; }
   virtual int rnd_end() { return 0; }
+  virtual int pre_rnd_end() { return 0; }
   virtual int write_row(uchar *buf __attribute__((unused)))
   {
     return HA_ERR_WRONG_COMMAND;
   }
+  virtual int pre_write_row(uchar *buf __attribute__((unused)))
+  {
+    return HA_ERR_WRONG_COMMAND;
+  }
 
   /**
     Update a single row.
@@ -3898,6 +3971,16 @@ private:
     return HA_ERR_WRONG_COMMAND;
   }
 
+  virtual int pre_direct_update_rows_init(
+    uint mode __attribute__((unused)),
+    KEY_MULTI_RANGE *ranges __attribute__((unused)),
+    uint range_count __attribute__((unused)),
+    bool sorted __attribute__((unused)),
+    uchar *new_data __attribute__((unused)))
+  {
+    return HA_ERR_WRONG_COMMAND;
+  }
+
   virtual int direct_update_rows(
     KEY_MULTI_RANGE *ranges __attribute__((unused)),
     uint range_count __attribute__((unused)),
@@ -3908,6 +3991,16 @@ private:
     return HA_ERR_WRONG_COMMAND;
   }
 
+  virtual int pre_direct_update_rows(
+    KEY_MULTI_RANGE *ranges __attribute__((unused)),
+    uint range_count __attribute__((unused)),
+    bool sorted __attribute__((unused)),
+    uchar *new_data __attribute__((unused)),
+    uint *update_rows __attribute__((unused)))
+  {
+    return HA_ERR_WRONG_COMMAND;
+  }
+
   virtual int direct_delete_rows_init(
     uint mode __attribute__((unused)),
     KEY_MULTI_RANGE *ranges __attribute__((unused)),
@@ -3917,6 +4010,15 @@ private:
     return HA_ERR_WRONG_COMMAND;
   }
 
+  virtual int pre_direct_delete_rows_init(
+    uint mode __attribute__((unused)),
+    KEY_MULTI_RANGE *ranges __attribute__((unused)),
+    uint range_count __attribute__((unused)),
+    bool sorted __attribute__((unused)))
+  {
+    return HA_ERR_WRONG_COMMAND;
+  }
+
   virtual int direct_delete_rows(
     KEY_MULTI_RANGE *ranges __attribute__((unused)),
     uint range_count __attribute__((unused)),
@@ -3926,6 +4028,15 @@ private:
     return HA_ERR_WRONG_COMMAND;
   }
 
+  virtual int pre_direct_delete_rows(
+    KEY_MULTI_RANGE *ranges __attribute__((unused)),
+    uint range_count __attribute__((unused)),
+    bool sorted __attribute__((unused)),
+    uint *delete_rows __attribute__((unused)))
+  {
+    return HA_ERR_WRONG_COMMAND;
+  }
+
   /**
     Reset state of file to after 'open'.
     This function is called after every statement for all tables used
