Commit 4e138572 authored by unknown's avatar unknown

Bug#20086: Can't get data from key partitioned tables with VARCHAR key

The problem appeared because the same values produced different hash
during INSERT and SELECT for VARCHAR data type.
Fix:
VARCHAR required special treatment to avoid hashing of length bytes
(leftmost one or two bytes) as well as trailing bytes beyond real length,
which could contain garbage. Fix is done by introducing hash() - new method
in the Field class.


mysql-test/r/partition_innodb.result:
  Adding test case
mysql-test/r/partition_pruning.result:
  Fixing test results (results differ due to changes in hash function)
mysql-test/t/partition_innodb.test:
  Adding test case
sql/field.cc:
  Adding generic hash() method, and a special
  method for VARCHAR.
sql/field.h:
  Adding prototypes for new methods
sql/key.cc:
  Mark columns for write before executinf of set_key_image().
  Thanks for Mikael for suggesting this fix.
sql/sql_partition.cc:
  Removing old hash code.
  Using new methid field->hash() instead.
parent 9addb8fd
...@@ -92,3 +92,18 @@ DROP TABLE IF EXISTS t1; ...@@ -92,3 +92,18 @@ DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t0_aux; DROP TABLE IF EXISTS t0_aux;
DROP TABLE IF EXISTS t0_definition; DROP TABLE IF EXISTS t0_definition;
DROP TABLE IF EXISTS t0_template; DROP TABLE IF EXISTS t0_template;
create table t1 (id varchar(64) primary key) engine=innodb
partition by key(id) partitions 5;
insert into t1 values ('a');
insert into t1 values ('aa');
insert into t1 values ('aaa');
select * from t1 where id = 'a';
id
a
select * from t1 where id = 'aa';
id
aa
select * from t1 where id = 'aaa';
id
aaa
drop table t1;
...@@ -31,7 +31,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra ...@@ -31,7 +31,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t2 p0,p1 ALL NULL NULL NULL NULL 3 Using where 1 SIMPLE t2 p0,p1 ALL NULL NULL NULL NULL 3 Using where
explain partitions select * from t2 where a=1 and b=1; explain partitions select * from t2 where a=1 and b=1;
id select_type table partitions type possible_keys key key_len ref rows Extra id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t2 p0 ALL NULL NULL NULL NULL 3 Using where 1 SIMPLE t2 p0 ALL NULL NULL NULL NULL 2 Using where
create table t3 ( create table t3 (
a int a int
) )
......
...@@ -66,3 +66,15 @@ DROP TABLE IF EXISTS t0_definition; ...@@ -66,3 +66,15 @@ DROP TABLE IF EXISTS t0_definition;
DROP TABLE IF EXISTS t0_template; DROP TABLE IF EXISTS t0_template;
--enable_warnings --enable_warnings
#
# Bug#20086: Can't get data from key partitioned tables with VARCHAR key
#
create table t1 (id varchar(64) primary key) engine=innodb
partition by key(id) partitions 5;
insert into t1 values ('a');
insert into t1 values ('aa');
insert into t1 values ('aaa');
select * from t1 where id = 'a';
select * from t1 where id = 'aa';
select * from t1 where id = 'aaa';
drop table t1;
...@@ -1243,6 +1243,21 @@ uint Field::offset() ...@@ -1243,6 +1243,21 @@ uint Field::offset()
} }
void Field::hash(ulong *nr, ulong *nr2)
{
if (is_null())
{
*nr^= (*nr << 1) | 1;
}
else
{
uint len= pack_length();
CHARSET_INFO *cs= charset();
cs->coll->hash_sort(cs, (uchar*) ptr, len, nr, nr2);
}
}
void Field::copy_from_tmp(int row_offset) void Field::copy_from_tmp(int row_offset)
{ {
memcpy(ptr,ptr+row_offset,pack_length()); memcpy(ptr,ptr+row_offset,pack_length());
...@@ -6923,6 +6938,21 @@ uint Field_varstring::is_equal(create_field *new_field) ...@@ -6923,6 +6938,21 @@ uint Field_varstring::is_equal(create_field *new_field)
} }
void Field_varstring::hash(ulong *nr, ulong *nr2)
{
if (is_null())
{
*nr^= (*nr << 1) | 1;
}
else
{
uint len= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
CHARSET_INFO *cs= charset();
cs->coll->hash_sort(cs, (uchar*) ptr + length_bytes, len, nr, nr2);
}
}
/**************************************************************************** /****************************************************************************
** blob type ** blob type
** A blob is saved as a length and a pointer. The length is stored in the ** A blob is saved as a length and a pointer. The length is stored in the
......
...@@ -351,6 +351,8 @@ public: ...@@ -351,6 +351,8 @@ public:
return field_length / charset()->mbmaxlen; return field_length / charset()->mbmaxlen;
} }
/* Hash value */
virtual void hash(ulong *nr, ulong *nr2);
friend bool reopen_table(THD *,struct st_table *,bool); friend bool reopen_table(THD *,struct st_table *,bool);
friend int cre_myisam(my_string name, register TABLE *form, uint options, friend int cre_myisam(my_string name, register TABLE *form, uint options,
ulonglong auto_increment_value); ulonglong auto_increment_value);
...@@ -1120,6 +1122,7 @@ public: ...@@ -1120,6 +1122,7 @@ public:
char *new_ptr, uchar *new_null_ptr, char *new_ptr, uchar *new_null_ptr,
uint new_null_bit); uint new_null_bit);
uint is_equal(create_field *new_field); uint is_equal(create_field *new_field);
void hash(ulong *nr, ulong *nr2);
}; };
......
...@@ -210,9 +210,13 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info, ...@@ -210,9 +210,13 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
} }
else if (key_part->key_part_flag & HA_VAR_LENGTH_PART) else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
{ {
my_bitmap_map *old_map;
key_length-= HA_KEY_BLOB_LENGTH; key_length-= HA_KEY_BLOB_LENGTH;
length= min(key_length, key_part->length); length= min(key_length, key_part->length);
old_map= dbug_tmp_use_all_columns(key_part->field->table,
key_part->field->table->write_set);
key_part->field->set_key_image((char *) from_key, length); key_part->field->set_key_image((char *) from_key, length);
dbug_tmp_restore_column_map(key_part->field->table->write_set, old_map);
from_key+= HA_KEY_BLOB_LENGTH; from_key+= HA_KEY_BLOB_LENGTH;
} }
else else
......
...@@ -2103,26 +2103,15 @@ static inline longlong part_val_int(Item *item_expr) ...@@ -2103,26 +2103,15 @@ static inline longlong part_val_int(Item *item_expr)
static uint32 calculate_key_value(Field **field_array) static uint32 calculate_key_value(Field **field_array)
{ {
uint32 hashnr= 0; ulong nr1= 1;
ulong nr2= 4; ulong nr2= 4;
do do
{ {
Field *field= *field_array; Field *field= *field_array;
if (field->is_null()) field->hash(&nr1, &nr2);
{
hashnr^= (hashnr << 1) | 1;
}
else
{
uint len= field->pack_length();
ulong nr1= 1;
CHARSET_INFO *cs= field->charset();
cs->coll->hash_sort(cs, (uchar*)field->ptr, len, &nr1, &nr2);
hashnr^= (uint32)nr1;
}
} while (*(++field_array)); } while (*(++field_array));
return hashnr; return (uint32) nr1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment