Commit df0b8e93 authored by Ramil Kalimullin's avatar Ramil Kalimullin

Fix for bug#47963: Wrong results when index is used

Problem: using null microsecond part in a WHERE condition 
(e.g. WHERE date_time_field <= "YYYY-MM-DD HH:MM:SS.0000") 
may lead to wrong results due to improper DATETIMEs 
comparison in some cases.

Fix: comparing DATETIMEs as strings we must trim trailing 0's
in such cases.
parents 8e931fe5 5a23617a
...@@ -2209,4 +2209,46 @@ EXPLAIN SELECT * FROM t1 FORCE INDEX(PRIMARY) WHERE b=1 AND c=1 ORDER BY a; ...@@ -2209,4 +2209,46 @@ EXPLAIN SELECT * FROM t1 FORCE INDEX(PRIMARY) WHERE b=1 AND c=1 ORDER BY a;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 4 NULL 128 Using where 1 SIMPLE t1 index NULL PRIMARY 4 NULL 128 Using where
DROP TABLE t1; DROP TABLE t1;
#
# Bug #47963: Wrong results when index is used
#
CREATE TABLE t1(
a VARCHAR(5) NOT NULL,
b VARCHAR(5) NOT NULL,
c DATETIME NOT NULL,
KEY (c)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES('TEST', 'TEST', '2009-10-09 00:00:00');
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00' AND c <= '2009-10-09 00:00:00';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.0' AND c <= '2009-10-09 00:00:00.0';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.0' AND c <= '2009-10-09 00:00:00';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00' AND c <= '2009-10-09 00:00:00.0';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.000' AND c <= '2009-10-09 00:00:00.000';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.00' AND c <= '2009-10-09 00:00:00.001';
a b c
TEST TEST 2009-10-09 00:00:00
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.001' AND c <= '2009-10-09 00:00:00.00';
a b c
EXPLAIN SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.001' AND c <= '2009-10-09 00:00:00.00';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
DROP TABLE t1;
End of 5.1 tests End of 5.1 tests
...@@ -461,4 +461,33 @@ EXPLAIN SELECT * FROM t1 FORCE INDEX(PRIMARY) WHERE b=1 AND c=1 ORDER BY a; ...@@ -461,4 +461,33 @@ EXPLAIN SELECT * FROM t1 FORCE INDEX(PRIMARY) WHERE b=1 AND c=1 ORDER BY a;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug #47963: Wrong results when index is used
--echo #
CREATE TABLE t1(
a VARCHAR(5) NOT NULL,
b VARCHAR(5) NOT NULL,
c DATETIME NOT NULL,
KEY (c)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES('TEST', 'TEST', '2009-10-09 00:00:00');
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00' AND c <= '2009-10-09 00:00:00';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.0' AND c <= '2009-10-09 00:00:00.0';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.0' AND c <= '2009-10-09 00:00:00';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00' AND c <= '2009-10-09 00:00:00.0';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.000' AND c <= '2009-10-09 00:00:00.000';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.00' AND c <= '2009-10-09 00:00:00.001';
SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.001' AND c <= '2009-10-09 00:00:00.00';
EXPLAIN SELECT * FROM t1 WHERE a = 'TEST' AND
c >= '2009-10-09 00:00:00.001' AND c <= '2009-10-09 00:00:00.00';
DROP TABLE t1;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -6900,17 +6900,37 @@ int stored_field_cmp_to_item(Field *field, Item *item) ...@@ -6900,17 +6900,37 @@ int stored_field_cmp_to_item(Field *field, Item *item)
/* /*
If comparing DATE with DATETIME, append the time-part to the DATE. If comparing DATE with DATETIME, append the time-part to the DATE.
So that the strings are equally formatted. So that the strings are equally formatted.
A DATE converted to string is 10 characters, and a DATETIME converted A DATE converted to string is 10 (MAX_DATE_WIDTH) characters,
to string is 19 characters. and a DATETIME converted to string is 19 (MAX_DATETIME_WIDTH) characters.
*/ */
field_type= field->type(); field_type= field->type();
uint32 item_length= item_result->length();
if (field_type == MYSQL_TYPE_DATE && if (field_type == MYSQL_TYPE_DATE &&
item_result->length() == 19) item_length == MAX_DATETIME_WIDTH)
field_tmp.append(" 00:00:00"); field_tmp.append(" 00:00:00");
else if (field_type == MYSQL_TYPE_DATETIME && else if (field_type == MYSQL_TYPE_DATETIME)
item_result->length() == 10) {
if (item_length == MAX_DATE_WIDTH)
item_result->append(" 00:00:00"); item_result->append(" 00:00:00");
else if (item_length > MAX_DATETIME_WIDTH)
{
/*
We don't store microsecond part of DATETIME in field
but item_result contains it. As we compare DATETIMEs as strings
we must trim trailing 0's in item_result's microsecond part
to ensure "YYYY-MM-DD HH:MM:SS" == "YYYY-MM-DD HH:MM:SS.0000"
*/
char *end= (char *) item_result->ptr() + item_length - 1;
/* Trim trailing 0's */
while (*end == '0')
end--;
/* Trim '.' if no microseconds */
if (*end == '.')
end--;
DBUG_ASSERT(end - item_result->ptr() + 1 >= MAX_DATETIME_WIDTH);
item_result->length(end - item_result->ptr() + 1);
}
}
return stringcmp(&field_tmp,item_result); return stringcmp(&field_tmp,item_result);
} }
if (res_type == INT_RESULT) if (res_type == INT_RESULT)
......
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