diff options
Diffstat (limited to 'sql/ha_partition.cc')
-rw-r--r-- | sql/ha_partition.cc | 283 |
1 files changed, 181 insertions, 102 deletions
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 36d5da94b11..b4181fc6d7f 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2005, 2011, Oracle and/or its affiliates. + Copyright (c) 2005, 2012, Oracle and/or its affiliates. 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 @@ -289,6 +289,7 @@ void ha_partition::init_handler_variables() m_is_sub_partitioned= 0; m_is_clone_of= NULL; m_clone_mem_root= NULL; + m_part_ids_sorted_by_num_of_records= NULL; #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; @@ -325,6 +326,7 @@ ha_partition::~ha_partition() } my_free(m_ordered_rec_buffer); m_ordered_rec_buffer= NULL; + my_free(m_part_ids_sorted_by_num_of_records); clear_handler_file(); @@ -1259,7 +1261,7 @@ bool ha_partition::check_and_repair(THD *thd) @retval FALSE Cannot be auto repaired */ -bool ha_partition::auto_repair() const +bool ha_partition::auto_repair(int error) const { DBUG_ENTER("ha_partition::auto_repair"); @@ -1267,7 +1269,7 @@ bool ha_partition::auto_repair() const As long as we only support one storage engine per table, we can use the first partition for this function. */ - DBUG_RETURN(m_file[0]->auto_repair()); + DBUG_RETURN(m_file[0]->auto_repair(error)); } @@ -2741,6 +2743,16 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_start_key.key= (const uchar*)ptr; } } + if (!m_part_ids_sorted_by_num_of_records) + { + if (!(m_part_ids_sorted_by_num_of_records= + (uint32*) my_malloc(m_tot_parts * sizeof(uint32), MYF(MY_WME)))) + DBUG_RETURN(error); + uint32 i; + /* Initialize it with all partition ids. */ + for (i= 0; i < m_tot_parts; i++) + m_part_ids_sorted_by_num_of_records[i]= i; + } /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) @@ -3670,14 +3682,14 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt) uint num_parts= m_part_info->num_parts; uint num_subparts= m_part_info->num_subparts; uint i= 0; - uint num_parts_set= alter_info->partition_names.elements; - uint num_parts_found= set_part_state(alter_info, m_part_info, - PART_ADMIN); DBUG_ENTER("ha_partition::truncate_partition"); /* Only binlog when it starts any call to the partitions handlers */ *binlog_stmt= false; + if (set_part_state(alter_info, m_part_info, PART_ADMIN)) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + /* TRUNCATE also means resetting auto_increment. Hence, reset it so that it will be initialized again at the next use. @@ -3687,10 +3699,6 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt) table_share->ha_part_data->auto_inc_initialized= FALSE; unlock_auto_increment(); - if (num_parts_set != num_parts_found && - (!(alter_info->flags & ALTER_ALL_PARTITION))) - DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); - *binlog_stmt= true; do @@ -5318,6 +5326,24 @@ int ha_partition::handle_ordered_prev(uchar *buf) and read_time calls */ +/** + Helper function for sorting according to number of rows in descending order. +*/ + +int ha_partition::compare_number_of_records(ha_partition *me, + const uint32 *a, + const uint32 *b) +{ + handler **file= me->m_file; + /* Note: sorting in descending order! */ + if (file[*a]->stats.records > file[*b]->stats.records) + return -1; + if (file[*a]->stats.records < file[*b]->stats.records) + return 1; + return 0; +} + + /* General method to gather info from handler @@ -5562,6 +5588,15 @@ int ha_partition::info(uint flag) } i++; } while (*(++file_array)); + /* + Sort the array of part_ids by number of records in + in descending order. + */ + my_qsort2((void*) m_part_ids_sorted_by_num_of_records, + m_tot_parts, + sizeof(uint32), + (qsort2_cmp) compare_number_of_records, + this); file= m_file[handler_instance]; file->info(HA_STATUS_CONST | no_lock_flag); @@ -6099,7 +6134,7 @@ int ha_partition::extra(enum ha_extra_function operation) 0 Success DESCRIPTION - Called at end of each statement to reste buffers + Called at end of each statement to reset buffers */ int ha_partition::reset(void) @@ -6317,21 +6352,73 @@ const key_map *ha_partition::keys_to_use_for_scanning() DBUG_RETURN(m_file[0]->keys_to_use_for_scanning()); } -#define MAX_PARTS_FOR_OPTIMIZER_CALLS 10 -/* - Prepare start variables for estimating optimizer costs. - @param[out] num_used_parts Number of partitions after pruning. - @param[out] check_min_num Number of partitions to call. - @param[out] first first used partition. +/** + Minimum number of rows to base optimizer estimate on. */ -void ha_partition::partitions_optimizer_call_preparations(uint *first, - uint *num_used_parts, - uint *check_min_num) + +ha_rows ha_partition::min_rows_for_estimate() { - *first= bitmap_get_first_set(&(m_part_info->used_partitions)); - *num_used_parts= bitmap_bits_set(&(m_part_info->used_partitions)); - *check_min_num= min(MAX_PARTS_FOR_OPTIMIZER_CALLS, *num_used_parts); + uint i, max_used_partitions, tot_used_partitions; + DBUG_ENTER("ha_partition::min_rows_for_estimate"); + + tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions); + DBUG_ASSERT(tot_used_partitions); + + /* + Allow O(log2(tot_partitions)) increase in number of used partitions. + This gives O(tot_rows/log2(tot_partitions)) rows to base the estimate on. + I.e when the total number of partitions doubles, allow one more + partition to be checked. + */ + i= 2; + max_used_partitions= 1; + while (i < m_tot_parts) + { + max_used_partitions++; + i= i << 1; + } + if (max_used_partitions > tot_used_partitions) + max_used_partitions= tot_used_partitions; + + /* stats.records is already updated by the info(HA_STATUS_VARIABLE) call. */ + DBUG_PRINT("info", ("max_used_partitions: %u tot_rows: %lu", + max_used_partitions, + (ulong) stats.records)); + DBUG_PRINT("info", ("tot_used_partitions: %u min_rows_to_check: %lu", + tot_used_partitions, + (ulong) stats.records * max_used_partitions + / tot_used_partitions)); + DBUG_RETURN(stats.records * max_used_partitions / tot_used_partitions); +} + + +/** + Get the biggest used partition. + + Starting at the N:th biggest partition and skips all non used + partitions, returning the biggest used partition found + + @param[in,out] part_index Skip the *part_index biggest partitions + + @return The biggest used partition with index not lower than *part_index. + @retval NO_CURRENT_PART_ID No more partition used. + @retval != NO_CURRENT_PART_ID partition id of biggest used partition with + index >= *part_index supplied. Note that + *part_index will be updated to the next + partition index to use. +*/ + +uint ha_partition::get_biggest_used_partition(uint *part_index) +{ + uint part_id; + while ((*part_index) < m_tot_parts) + { + part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++]; + if (bitmap_is_set(&m_part_info->used_partitions, part_id)) + return part_id; + } + return NO_CURRENT_PART_ID; } @@ -6347,115 +6434,107 @@ void ha_partition::partitions_optimizer_call_preparations(uint *first, double ha_partition::scan_time() { - double scan_time= 0.0; - uint first, part_id, num_used_parts, check_min_num, partitions_called= 0; + double scan_time= 0; + handler **file; DBUG_ENTER("ha_partition::scan_time"); - partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num); - for (part_id= first; partitions_called < num_used_parts ; part_id++) - { - if (!bitmap_is_set(&(m_part_info->used_partitions), part_id)) - continue; - scan_time+= m_file[part_id]->scan_time(); - partitions_called++; - if (partitions_called >= check_min_num && scan_time != 0.0) - { - DBUG_RETURN(scan_time * - (double) num_used_parts / (double) partitions_called); - } - } + for (file= m_file; *file; file++) + if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) + scan_time+= (*file)->scan_time(); DBUG_RETURN(scan_time); } -/* - Estimate rows for records_in_range or estimate_rows_upper_bound. +/** + Find number of records in a range. + @param inx Index number + @param min_key Start of range + @param max_key End of range - @param is_records_in_range call records_in_range instead of - estimate_rows_upper_bound. - @param inx (only for records_in_range) index to use. - @param min_key (only for records_in_range) start of range. - @param max_key (only for records_in_range) end of range. + @return Number of rows in range. - @return Number of rows or HA_POS_ERROR. + Given a starting key, and an ending key estimate the number of rows that + will exist between the two. max_key may be empty which in case determine + if start_key matches any rows. */ -ha_rows ha_partition::estimate_rows(bool is_records_in_range, uint inx, - key_range *min_key, key_range *max_key) + +ha_rows ha_partition::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { - ha_rows rows, estimated_rows= 0; - uint first, part_id, num_used_parts, check_min_num, partitions_called= 0; + ha_rows min_rows_to_check, rows, estimated_rows=0, checked_rows= 0; + uint partition_index= 0, part_id; DBUG_ENTER("ha_partition::records_in_range"); - partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num); - for (part_id= first; partitions_called < num_used_parts ; part_id++) + min_rows_to_check= min_rows_for_estimate(); + + while ((part_id= get_biggest_used_partition(&partition_index)) + != NO_CURRENT_PART_ID) { - if (!bitmap_is_set(&(m_part_info->used_partitions), part_id)) - continue; - if (is_records_in_range) - rows= m_file[part_id]->records_in_range(inx, min_key, max_key); - else - rows= m_file[part_id]->estimate_rows_upper_bound(); + rows= m_file[part_id]->records_in_range(inx, min_key, max_key); + + DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows, + (ulong) m_file[part_id]->stats.records)); + if (rows == HA_POS_ERROR) DBUG_RETURN(HA_POS_ERROR); estimated_rows+= rows; - partitions_called++; - if (partitions_called >= check_min_num && estimated_rows) + checked_rows+= m_file[part_id]->stats.records; + /* + Returning 0 means no rows can be found, so we must continue + this loop as long as we have estimated_rows == 0. + Also many engines return 1 to indicate that there may exist + a matching row, we do not normalize this by dividing by number of + used partitions, but leave it to be returned as a sum, which will + reflect that we will need to scan each partition's index. + + Note that this statistics may not always be correct, so we must + continue even if the current partition has 0 rows, since we might have + deleted rows from the current partition, or inserted to the next + partition. + */ + if (estimated_rows && checked_rows && + checked_rows >= min_rows_to_check) { - DBUG_RETURN(estimated_rows * num_used_parts / partitions_called); + DBUG_PRINT("info", + ("records_in_range(inx %u): %lu (%lu * %lu / %lu)", + inx, + (ulong) (estimated_rows * stats.records / checked_rows), + (ulong) estimated_rows, + (ulong) stats.records, + (ulong) checked_rows)); + DBUG_RETURN(estimated_rows * stats.records / checked_rows); } } + DBUG_PRINT("info", ("records_in_range(inx %u): %lu", + inx, + (ulong) estimated_rows)); DBUG_RETURN(estimated_rows); } -/* - Find number of records in a range - - SYNOPSIS - records_in_range() - inx Index number - min_key Start of range - max_key End of range - - RETURN VALUE - Number of rows in range - - DESCRIPTION - Given a starting key, and an ending key estimate the number of rows that - will exist between the two. end_key may be empty which in case determine - if start_key matches any rows. - - Called from opt_range.cc by check_quick_keys(). - - monty: MUST be called for each range and added. - Note that MySQL will assume that if this returns 0 there is no - matching rows for the range! -*/ - -ha_rows ha_partition::records_in_range(uint inx, key_range *min_key, - key_range *max_key) -{ - DBUG_ENTER("ha_partition::records_in_range"); - - DBUG_RETURN(estimate_rows(TRUE, inx, min_key, max_key)); -} - - -/* - Estimate upper bound of number of rows - - SYNOPSIS - estimate_rows_upper_bound() +/** + Estimate upper bound of number of rows. - RETURN VALUE - Number of rows + @return Number of rows. */ ha_rows ha_partition::estimate_rows_upper_bound() { + ha_rows rows, tot_rows= 0; + handler **file= m_file; DBUG_ENTER("ha_partition::estimate_rows_upper_bound"); - DBUG_RETURN(estimate_rows(FALSE, 0, NULL, NULL)); + do + { + if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) + { + rows= (*file)->estimate_rows_upper_bound(); + if (rows == HA_POS_ERROR) + DBUG_RETURN(HA_POS_ERROR); + tot_rows+= rows; + } + } while (*(++file)); + DBUG_RETURN(tot_rows); } |