diff --git a/mysql-test/r/partition_range.result b/mysql-test/r/partition_range.result index 9812c80040bac463c79ee90a07209c1d20a1f3ed..2fa40b44815e9461ed41a25809761c29360d4ddd 100644 --- a/mysql-test/r/partition_range.result +++ b/mysql-test/r/partition_range.result @@ -709,3 +709,45 @@ WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR id select_type table partitions type possible_keys key key_len ref rows Extra 1 SIMPLE t1 p407,p408,p409,p507,p508,p509 ALL NULL NULL NULL NULL 18 Using where DROP TABLE t1; +create table t1 (a varchar(20)) +partition by range (crc32(md5(a))) +(partition p0 values less than (100), +partition p1 values less than maxvalue); +insert into t1 values ("12345678901234567890"); +insert into t1 values ("A2345678901234567890"); +insert into t1 values ("B2345678901234567890"); +insert into t1 values ("1234567890123456789"); +insert into t1 values ("1234567890123456"); +select * from t1; +a +12345678901234567890 +A2345678901234567890 +B2345678901234567890 +1234567890123456789 +1234567890123456 +explain partitions select * from t1 where a = "12345678901234567890"; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 5 Using where +explain partitions select * from t1 where a = "12345678901234567890" OR +a = "A2345678901234567890" OR +a = "B2345678901234567890" OR +a = "C2345678901234567890"; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 5 Using where +explain partitions select * from t1 where a = "01234567890123456"; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 5 Using where +select * from t1 where a = "01234567890123456"; +a +select * from t1 where a = "12345678901234567890" OR +a = "A2345678901234567890" OR +a = "B2345678901234567890" OR +a = "C2345678901234567890"; +a +12345678901234567890 +A2345678901234567890 +B2345678901234567890 +select * from t1 where a = "12345678901234567890"; +a +12345678901234567890 +drop table t1; diff --git a/mysql-test/t/partition_range.test b/mysql-test/t/partition_range.test index 670b9333ab99242e5a5d11a32598e231cf1631ff..8cf2313e39cf4e3c892648e85e221435fbd38513 100644 --- a/mysql-test/t/partition_range.test +++ b/mysql-test/t/partition_range.test @@ -686,3 +686,33 @@ EXPLAIN PARTITIONS SELECT * from t1 WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR (a >= '2005-07-01' AND a <= '2005-09-30'); DROP TABLE t1; + +# +# Bug 18198: Try with a couple of cases using VARCHAR fields in +# partition function. +create table t1 (a varchar(20)) +partition by range (crc32(md5(a))) +(partition p0 values less than (100), + partition p1 values less than maxvalue); + +insert into t1 values ("12345678901234567890"); +insert into t1 values ("A2345678901234567890"); +insert into t1 values ("B2345678901234567890"); +insert into t1 values ("1234567890123456789"); +insert into t1 values ("1234567890123456"); +select * from t1; +explain partitions select * from t1 where a = "12345678901234567890"; +explain partitions select * from t1 where a = "12345678901234567890" OR + a = "A2345678901234567890" OR + a = "B2345678901234567890" OR + a = "C2345678901234567890"; +explain partitions select * from t1 where a = "01234567890123456"; +select * from t1 where a = "01234567890123456"; +select * from t1 where a = "12345678901234567890" OR + a = "A2345678901234567890" OR + a = "B2345678901234567890" OR + a = "C2345678901234567890"; +select * from t1 where a = "12345678901234567890"; + + +drop table t1; diff --git a/sql/partition_info.h b/sql/partition_info.h index 8cc989c09919469dc7af659b6021443fb90578bc..91beaf569e0242de11ac1d503990dff9a92b1dc7 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -60,7 +60,17 @@ public: same in all subpartitions */ get_subpart_id_func get_subpartition_id; - + + /* + When we have various string fields we might need some preparation + before and clean-up after calling the get_part_id_func's. We need + one such method for get_partition_id and one for + get_part_partition_id and one for get_subpartition_id. + */ + get_part_id_func get_partition_id_charset; + get_part_id_func get_part_partition_id_charset; + get_subpart_id_func get_subpartition_id_charset; + /* NULL-terminated array of fields used in partitioned expression */ Field **part_field_array; /* NULL-terminated array of fields used in subpartitioned expression */ @@ -72,6 +82,16 @@ public: */ Field **full_part_field_array; + /* + When we have a field that requires transformation before calling the + partition functions we must allocate field buffers for the field of + the fields in the partition function. + */ + char **part_field_buffers; + char **subpart_field_buffers; + char **restore_part_field_ptrs; + char **restore_subpart_field_ptrs; + Item *part_expr; Item *subpart_expr; @@ -188,6 +208,8 @@ public: bool is_auto_partitioned; bool from_openfrm; bool has_null_value; + bool includes_charset_field_part; + bool includes_charset_field_subpart; partition_info() @@ -195,6 +217,8 @@ public: get_subpartition_id(NULL), part_field_array(NULL), subpart_field_array(NULL), full_part_field_array(NULL), + part_field_buffers(NULL), subpart_field_buffers(NULL), + restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL), part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), list_array(NULL), @@ -217,7 +241,8 @@ public: list_of_part_fields(FALSE), list_of_subpart_fields(FALSE), linear_hash_ind(FALSE), fixed(FALSE), is_auto_partitioned(FALSE), from_openfrm(FALSE), - has_null_value(FALSE) + has_null_value(FALSE), includes_charset_field_part(FALSE), + includes_charset_field_subpart(FALSE) { all_fields_in_PF.clear_all(); all_fields_in_PPF.clear_all(); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 54610328694acd900b7aa375a059ce60b4ec7251..b2a6287ff8108b1516e2358e234152fbd9bf7cd9 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -62,48 +62,64 @@ static const char *end_paren_str= ")"; static const char *begin_paren_str= "("; static const char *comma_str= ","; +static int get_part_id_charset_func_all(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +static int get_part_id_charset_func_part(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +static int get_part_id_charset_func_subpart(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +static int get_part_part_id_charset_func(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +static uint32 get_subpart_id_charset_func(partition_info *part_info); +int get_partition_id_list(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_list(partition_info *part_info, + uint32 *part_id, + longlong *func_value); +int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value); -int get_partition_id_range(partition_info *part_info, - uint32 *part_id, - longlong *func_value); int get_partition_id_hash_nosub(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_key_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_key_nosub(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_linear_hash_nosub(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_linear_key_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_linear_key_nosub(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_range_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_range_sub_key(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_range_sub_key(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_range_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_range_sub_linear_key(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_range_sub_linear_key(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_list_sub_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_key(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_list_sub_key(partition_info *part_info, + uint32 *part_id, + longlong *func_value); int get_partition_id_list_sub_linear_hash(partition_info *part_info, - uint32 *part_id, - longlong *func_value); -int get_partition_id_list_sub_linear_key(partition_info *part_info, uint32 *part_id, longlong *func_value); +int get_partition_id_list_sub_linear_key(partition_info *part_info, + uint32 *part_id, + longlong *func_value); uint32 get_partition_id_hash_sub(partition_info *part_info); uint32 get_partition_id_key_sub(partition_info *part_info); uint32 get_partition_id_linear_hash_sub(partition_info *part_info); @@ -1311,6 +1327,34 @@ static void set_up_partition_func_pointers(partition_info *part_info) } } } + if (part_info->includes_charset_field_part || + part_info->includes_charset_field_subpart) + { + DBUG_ASSERT(part_info->get_partition_id); + part_info->get_partition_id_charset= part_info->get_partition_id; + if (part_info->includes_charset_field_part && + part_info->includes_charset_field_subpart) + part_info->get_partition_id= get_part_id_charset_func_all; + else if (part_info->includes_charset_field_part) + part_info->get_partition_id= get_part_id_charset_func_part; + else + part_info->get_partition_id= get_part_id_charset_func_subpart; + } + if (part_info->includes_charset_field_part && + part_info->is_sub_partitioned()) + { + DBUG_ASSERT(part_info->get_part_partition_id); + part_info->get_part_partition_id_charset= + part_info->get_part_partition_id; + part_info->get_part_partition_id= get_part_part_id_charset_func; + } + if (part_info->includes_charset_field_subpart) + { + DBUG_ASSERT(part_info->get_subpartition_id); + part_info->get_subpartition_id_charset= + part_info->get_subpartition_id; + part_info->get_subpartition_id= get_subpart_id_charset_func; + } DBUG_VOID_RETURN; } @@ -1377,16 +1421,24 @@ static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask, character sets and collations. SYNOPSIS check_part_func_fields() - part_info Partition info ptr Array of Field pointers + ok_with_charsets Will we report allowed charset + fields as ok RETURN VALUES FALSE Success TRUE Error + DESCRIPTION + We will check in this routine that the fields of the partition functions + do not contain unallowed parts. It can also be used to check if there + are fields that require special care by calling my_strnxfrm before + calling the functions to calculate partition id. */ -static bool check_part_func_fields(Field **ptr) +static bool check_part_func_fields(Field **ptr, bool ok_with_charsets) { Field *field; + DBUG_ENTER("check_part_func_field"); + while ((field= *(ptr++))) { /* @@ -1400,13 +1452,121 @@ static bool check_part_func_fields(Field **ptr) CHARSET_INFO *cs= ((Field_str*)field)->charset(); if (field->type() == MYSQL_TYPE_STRING && cs->state & MY_CS_BINSORT) - return FALSE; - return TRUE; + { + DBUG_RETURN(FALSE); + } + if (!ok_with_charsets || + cs->mbmaxlen > 1 || + cs->strxfrm_multiply > 1) + { + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); } } - return FALSE; + DBUG_RETURN(FALSE); } +/* + Set up buffers and arrays for fields requiring preparation + SYNOPSIS + set_up_charset_field_preps() + part_info Partition info object + RETURN VALUES + TRUE Memory Allocation error + FALSE Success + DESCRIPTION + Set up arrays and buffers for fields that require special care for + calculation of partition id. This is used for string fields with + variable length or string fields with fixed length that isn't using + the binary collation. +*/ + +static bool set_up_charset_field_preps(partition_info *part_info) +{ + Field *field, **ptr; + char *field_buf; + char **char_ptrs; + unsigned i; + size_t size; + + DBUG_ENTER("set_up_charset_field_preps"); + if (check_part_func_fields(part_info->part_field_array, FALSE)) + { + ptr= part_info->part_field_array; + part_info->includes_charset_field_part= TRUE; + /* + Set up arrays and buffers for those fields + */ + i= 0; + while ((field= *(ptr++))) + i++; + size= i * sizeof(char*); + + if (!(char_ptrs= (char**)sql_calloc(size))) + goto error; + part_info->part_field_buffers= char_ptrs; + + if (!(char_ptrs= (char**)sql_calloc(size))) + goto error; + part_info->restore_part_field_ptrs= char_ptrs; + + ptr= part_info->part_field_array; + i= 0; + while ((field= *(ptr++))) + { + CHARSET_INFO *cs= ((Field_str*)field)->charset(); + size= field->pack_length(); + if (!(field_buf= sql_calloc(size))) + goto error; + part_info->part_field_buffers[i++]= field_buf; + } + } + if (part_info->is_sub_partitioned() && + check_part_func_fields(part_info->subpart_field_array, FALSE)) + { + /* + Set up arrays and buffers for those fields + */ + part_info->includes_charset_field_subpart= TRUE; + + ptr= part_info->subpart_field_array; + i= 0; + while ((field= *(ptr++))) + { + unsigned j= 0; + Field *part_field; + Field **part_ptr= part_info->part_field_array; + bool field_already_have_buffer= FALSE; + CHARSET_INFO *cs= ((Field_str*)field)->charset(); + size= field->pack_length(); + + while ((part_field= *(part_ptr++))) + { + field_buf= part_info->part_field_buffers[j++]; + if (field == part_field) + { + field_already_have_buffer= TRUE; + break; + } + } + if (!field_already_have_buffer) + { + if (!(field_buf= sql_calloc(size))) + goto error; + } + part_info->subpart_field_buffers[i++]= field_buf; + } + size= i * sizeof(char*); + if (!(char_ptrs= (char**)sql_calloc(i * sizeof(char*)))) + goto error; + part_info->restore_subpart_field_ptrs= char_ptrs; + } + DBUG_RETURN(FALSE); +error: + mem_alloc_error(size); + DBUG_RETURN(TRUE); +} /* fix partition functions @@ -1555,10 +1715,10 @@ bool fix_partition_func(THD *thd, TABLE *table, } if (((part_info->part_type != HASH_PARTITION || part_info->list_of_part_fields == FALSE) && - check_part_func_fields(part_info->part_field_array)) || + check_part_func_fields(part_info->part_field_array, TRUE)) || (part_info->list_of_part_fields == FALSE && part_info->is_sub_partitioned() && - check_part_func_fields(part_info->subpart_field_array))) + check_part_func_fields(part_info->subpart_field_array, TRUE))) { my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); goto end; @@ -1573,6 +1733,11 @@ bool fix_partition_func(THD *thd, TABLE *table, goto end; if (unlikely(set_up_partition_bitmap(thd, part_info))) goto end; + if (unlikely(set_up_charset_field_preps(part_info))) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + goto end; + } check_range_capable_PF(table); set_up_partition_key_maps(table, part_info); set_up_partition_func_pointers(part_info); @@ -2289,6 +2454,86 @@ static uint32 get_part_id_linear_key(partition_info *part_info, no_parts)); } +/* + Copy to field buffers and set up field pointers + SYNOPSIS + copy_to_part_field_buffers() + ptr Array of fields to copy + RETURN VALUES + NONE + DESCRIPTION + This routine is used to take the data from field pointer, convert + it to a standard format and store this format in a field buffer + allocated for this purpose. Next the field pointers are moved to + point to the field buffers. There is a separate to restore the + field pointers after this call. +*/ + +static void copy_to_part_field_buffers(Field **ptr, + char **field_bufs, + char **restore_ptr) +{ + Field *field; + while ((field= *(ptr++))) + { + *restore_ptr= field->ptr; + restore_ptr++; + if ((field->type() == MYSQL_TYPE_VARCHAR || + (field->type() == MYSQL_TYPE_STRING && + (!(((Field_str*)field)->charset()->state & MY_CS_BINSORT))) && + ((!field->maybe_null()) || (!field->is_null())))) + { + CHARSET_INFO *cs= ((Field_str*)field)->charset(); + uint len= field->pack_length(); + char *field_buf= *field_bufs; + /* + We only use the field buffer for VARCHAR and CHAR strings + which isn't of a binary collation. We also only use the + field buffer for fields which are not currently NULL. + The field buffer will store a normalised string. We use + the strnxfrm method to normalise the string. + */ + if (field->type() == MYSQL_TYPE_VARCHAR) + { + uint len_bytes= ((Field_varstring*)field)->length_bytes; + my_strnxfrm(cs, (uchar*)(field_buf + len_bytes), (len - len_bytes), + (uchar*)(field->ptr + len_bytes), field->field_length); + if (len_bytes == 1) + *field_buf= (uchar)field->field_length; + else + int2store(field_buf, field->field_length); + } + else + { + my_strnxfrm(cs, (uchar*)field_buf, len, + (uchar*)field->ptr, field->field_length); + } + field->ptr= field_buf; + } + field_bufs++; + } + return; +} + +/* + Restore field pointers + SYNOPSIS + restore_part_field_pointers() + ptr Array of fields to restore + RETURN VALUES + NONE +*/ + +static void restore_part_field_pointers(Field **ptr, char **restore_ptr) +{ + Field *field; + while ((field= *(ptr++))) + { + field->ptr= *restore_ptr; + restore_ptr++; + } + return; +} /* This function is used to calculate the partition id where all partition fields have been prepared to point to a record where the partition field @@ -2299,6 +2544,7 @@ static uint32 get_part_id_linear_key(partition_info *part_info, part_info A reference to the partition_info struct where all the desired information is given out:part_id The partition id is returned through this pointer + out: func_value Value of partition function (longlong) RETURN VALUE part_id Partition id of partition that would contain @@ -2342,6 +2588,7 @@ static uint32 get_part_id_linear_key(partition_info *part_info, part_info A reference to the partition_info struct where all the desired information is given out:part_id The partition id is returned through this pointer + out: func_value The value calculated by partition function RETURN VALUE part_id Partition id of partition that would contain @@ -2363,6 +2610,78 @@ static uint32 get_part_id_linear_key(partition_info *part_info, get_partition_id_linear_key_nosub */ +static int get_part_id_charset_func_subpart(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + int res; + copy_to_part_field_buffers(part_info->subpart_field_array, + part_info->subpart_field_buffers, + part_info->restore_subpart_field_ptrs); + res= part_info->get_partition_id_charset(part_info, part_id, func_value); + restore_part_field_pointers(part_info->subpart_field_array, + part_info->restore_subpart_field_ptrs); + return res; +} +static int get_part_id_charset_func_part(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + int res; + copy_to_part_field_buffers(part_info->part_field_array, + part_info->part_field_buffers, + part_info->restore_part_field_ptrs); + res= part_info->get_partition_id_charset(part_info, part_id, func_value); + restore_part_field_pointers(part_info->part_field_array, + part_info->restore_part_field_ptrs); + return res; +} + +static int get_part_id_charset_func_all(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + int res; + copy_to_part_field_buffers(part_info->part_field_array, + part_info->part_field_buffers, + part_info->restore_part_field_ptrs); + copy_to_part_field_buffers(part_info->subpart_field_array, + part_info->subpart_field_buffers, + part_info->restore_subpart_field_ptrs); + res= part_info->get_partition_id_charset(part_info, part_id, func_value); + restore_part_field_pointers(part_info->part_field_array, + part_info->restore_part_field_ptrs); + restore_part_field_pointers(part_info->subpart_field_array, + part_info->restore_subpart_field_ptrs); + return res; +} + +static int get_part_part_id_charset_func(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + int res; + copy_to_part_field_buffers(part_info->part_field_array, + part_info->part_field_buffers, + part_info->restore_part_field_ptrs); + res= part_info->get_part_partition_id_charset(part_info, + part_id, func_value); + restore_part_field_pointers(part_info->part_field_array, + part_info->restore_part_field_ptrs); + return res; +} + +static uint32 get_subpart_id_charset_func(partition_info *part_info) +{ + int res; + copy_to_part_field_buffers(part_info->subpart_field_array, + part_info->subpart_field_buffers, + part_info->restore_subpart_field_ptrs); + res= part_info->get_subpartition_id_charset(part_info); + restore_part_field_pointers(part_info->subpart_field_array, + part_info->restore_subpart_field_ptrs); + return res; +} int get_partition_id_list(partition_info *part_info, uint32 *part_id, @@ -2451,6 +2770,21 @@ notfound: The edge of corresponding sub-array of part_info->list_array */ +uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info, + bool left_endpoint, + bool include_endpoint) +{ + uint32 res; + copy_to_part_field_buffers(part_info->part_field_array, + part_info->part_field_buffers, + part_info->restore_part_field_ptrs); + res= get_list_array_idx_for_endpoint(part_info, left_endpoint, + include_endpoint); + restore_part_field_pointers(part_info->part_field_array, + part_info->restore_part_field_ptrs); + return res; +} + uint32 get_list_array_idx_for_endpoint(partition_info *part_info, bool left_endpoint, bool include_endpoint) @@ -2580,6 +2914,22 @@ int get_partition_id_range(partition_info *part_info, The edge of corresponding part_info->range_int_array sub-array. */ +static uint32 +get_partition_id_range_for_endpoint_charset(partition_info *part_info, + bool left_endpoint, + bool include_endpoint) +{ + uint32 res; + copy_to_part_field_buffers(part_info->part_field_array, + part_info->part_field_buffers, + part_info->restore_part_field_ptrs); + res= get_partition_id_range_for_endpoint(part_info, left_endpoint, + include_endpoint); + restore_part_field_pointers(part_info->part_field_array, + part_info->restore_part_field_ptrs); + return res; +} + uint32 get_partition_id_range_for_endpoint(partition_info *part_info, bool left_endpoint, bool include_endpoint) @@ -6420,13 +6770,20 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, if (part_info->part_type == RANGE_PARTITION) { - get_endpoint= get_partition_id_range_for_endpoint; + if (part_info->includes_charset_field_part) + get_endpoint= get_partition_id_range_for_endpoint_charset; + else + get_endpoint= get_partition_id_range_for_endpoint; max_endpoint_val= part_info->no_parts; part_iter->get_next= get_next_partition_id_range; } else if (part_info->part_type == LIST_PARTITION) { - get_endpoint= get_list_array_idx_for_endpoint; + + if (part_info->includes_charset_field_part) + get_endpoint= get_list_array_idx_for_endpoint_charset; + else + get_endpoint= get_list_array_idx_for_endpoint; max_endpoint_val= part_info->no_list_values; part_iter->get_next= get_next_partition_id_list; part_iter->part_info= part_info;