Commit 6866ca00 authored by unknown's avatar unknown

BUG#24342 - Incorrect results with query over MERGE table

MERGE engine may return incorrect values when several representations
of equal keys are present in the index. For example "groß" and "gross"
or "gross" and "gross " (trailing space), which are considered equal,
but have different lengths.

The problem was that key length was not recalculated after key lookup.

Only MERGE engine is affected.


myisam/mi_rkey.c:
  info->lastkey gets rewritten by mi_search. Later we recalculate found lastkey
  length. This is done to make sure that mi_rnext_same gets true, found (not
  searched) lastkey length. Searched and found key lengths may be different,
  for example in case searched key is "groß" and found is "gross" or in case
  a key has trailing spaces.
  
  Unfortunately we recalculate found lastkey length only for first
  underlying table. To recalculate found key length for non-first underlying
  table we need to know how much key segments were used to create this key.
  
  When mi_rkey is called for first underlying table of a merge table, store
  offset to last used key segment.
  
  Restore last_used_keyseg variable when mi_rkey is called for non-first
  underlying table.
myisam/myisamdef.h:
  Added last_used_keyseg variable to MI_INFO. It is used by merge engine to calculate
  key length.
myisammrg/myrg_rkey.c:
  Pass last used key segment returned by first table key read to other
  table key reads.
mysql-test/r/merge.result:
  A test case for bug#24342.
mysql-test/t/merge.test:
  A test case for bug#24342.
parent f6eca60a
...@@ -50,7 +50,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, ...@@ -50,7 +50,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len,
key_buff=info->lastkey+info->s->base.max_key_length; key_buff=info->lastkey+info->s->base.max_key_length;
pack_key_length= key_len; pack_key_length= key_len;
bmove(key_buff,key,key_len); bmove(key_buff,key,key_len);
last_used_keyseg= 0; last_used_keyseg= info->s->keyinfo[inx].seg + info->last_used_keyseg;
} }
else else
{ {
...@@ -62,6 +62,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, ...@@ -62,6 +62,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len,
key_len, &last_used_keyseg); key_len, &last_used_keyseg);
/* Save packed_key_length for use by the MERGE engine. */ /* Save packed_key_length for use by the MERGE engine. */
info->pack_key_length= pack_key_length; info->pack_key_length= pack_key_length;
info->last_used_keyseg= last_used_keyseg - info->s->keyinfo[inx].seg;
DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE, keyinfo->seg, DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE, keyinfo->seg,
key_buff, pack_key_length);); key_buff, pack_key_length););
} }
......
...@@ -263,6 +263,7 @@ struct st_myisam_info { ...@@ -263,6 +263,7 @@ struct st_myisam_info {
enum ha_rkey_function last_key_func; /* CONTAIN, OVERLAP, etc */ enum ha_rkey_function last_key_func; /* CONTAIN, OVERLAP, etc */
uint save_lastkey_length; uint save_lastkey_length;
uint pack_key_length; /* For MYISAMMRG */ uint pack_key_length; /* For MYISAMMRG */
uint16 last_used_keyseg; /* For MyISAMMRG */
int errkey; /* Got last error on this key */ int errkey; /* Got last error on this key */
int lock_type; /* How database was locked */ int lock_type; /* How database was locked */
int tmp_lock_type; /* When locked by readinfo */ int tmp_lock_type; /* When locked by readinfo */
......
...@@ -41,12 +41,14 @@ int myrg_rkey(MYRG_INFO *info,byte *buf,int inx, const byte *key, ...@@ -41,12 +41,14 @@ int myrg_rkey(MYRG_INFO *info,byte *buf,int inx, const byte *key,
{ {
byte *key_buff; byte *key_buff;
uint pack_key_length; uint pack_key_length;
uint16 last_used_keyseg;
MYRG_TABLE *table; MYRG_TABLE *table;
MI_INFO *mi; MI_INFO *mi;
int err; int err;
DBUG_ENTER("myrg_rkey"); DBUG_ENTER("myrg_rkey");
LINT_INIT(key_buff); LINT_INIT(key_buff);
LINT_INIT(pack_key_length); LINT_INIT(pack_key_length);
LINT_INIT(last_used_keyseg);
if (_myrg_init_queue(info,inx,search_flag)) if (_myrg_init_queue(info,inx,search_flag))
DBUG_RETURN(my_errno); DBUG_RETURN(my_errno);
...@@ -61,10 +63,12 @@ int myrg_rkey(MYRG_INFO *info,byte *buf,int inx, const byte *key, ...@@ -61,10 +63,12 @@ int myrg_rkey(MYRG_INFO *info,byte *buf,int inx, const byte *key,
/* Get the saved packed key and packed key length. */ /* Get the saved packed key and packed key length. */
key_buff=(byte*) mi->lastkey+mi->s->base.max_key_length; key_buff=(byte*) mi->lastkey+mi->s->base.max_key_length;
pack_key_length=mi->pack_key_length; pack_key_length=mi->pack_key_length;
last_used_keyseg= mi->last_used_keyseg;
} }
else else
{ {
mi->once_flags|= USE_PACKED_KEYS; mi->once_flags|= USE_PACKED_KEYS;
mi->last_used_keyseg= last_used_keyseg;
err=mi_rkey(mi,0,inx,key_buff,pack_key_length,search_flag); err=mi_rkey(mi,0,inx,key_buff,pack_key_length,search_flag);
} }
info->last_used_table=table+1; info->last_used_table=table+1;
......
...@@ -819,3 +819,14 @@ ALTER TABLE m1 ENGINE=MERGE UNION=(t1); ...@@ -819,3 +819,14 @@ ALTER TABLE m1 ENGINE=MERGE UNION=(t1);
SELECT * FROM m1; SELECT * FROM m1;
c1 c2 c3 c4 c5 c6 c7 c8 c9 c1 c2 c3 c4 c5 c6 c7 c8 c9
DROP TABLE t1, m1; DROP TABLE t1, m1;
CREATE TABLE t1 (a VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_german2_ci,
b INT, INDEX(a,b));
CREATE TABLE t2 LIKE t1;
CREATE TABLE t3 LIKE t1;
ALTER TABLE t3 ENGINE=MERGE UNION=(t1,t2);
INSERT INTO t1 VALUES ('ss',1);
INSERT INTO t2 VALUES ('ss',2),(0xDF,2);
SELECT COUNT(*) FROM t3 WHERE a=0xDF AND b=2;
COUNT(*)
2
DROP TABLE t1,t2,t3;
...@@ -454,4 +454,17 @@ ALTER TABLE m1 ENGINE=MERGE UNION=(t1); ...@@ -454,4 +454,17 @@ ALTER TABLE m1 ENGINE=MERGE UNION=(t1);
SELECT * FROM m1; SELECT * FROM m1;
DROP TABLE t1, m1; DROP TABLE t1, m1;
#
# BUG#24342 - Incorrect results with query over MERGE table
#
CREATE TABLE t1 (a VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_german2_ci,
b INT, INDEX(a,b));
CREATE TABLE t2 LIKE t1;
CREATE TABLE t3 LIKE t1;
ALTER TABLE t3 ENGINE=MERGE UNION=(t1,t2);
INSERT INTO t1 VALUES ('ss',1);
INSERT INTO t2 VALUES ('ss',2),(0xDF,2);
SELECT COUNT(*) FROM t3 WHERE a=0xDF AND b=2;
DROP TABLE t1,t2,t3;
# End of 4.1 tests # End of 4.1 tests
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