Commit 058d2296 authored by evgen@sunlight.local's avatar evgen@sunlight.local

type_date.result:

  Added the test case for bug#21677: Wrong result when comparing a DATE and a DATETIME in BETWEEN
  Corrected a test case after removal of fix for bug#16377
query_cache.result, func_time.test, view.result, view.test, func_time.result:
  Corrected a test case after removal of fix for bug#16377
type_date.test:
  Added the test case for bug#21677: Wrong result when comparing a DATE and a DATETIME in BETWEEN
   Corrected a test case after removal of fix for bug#16377
item_cmpfunc.cc:
  Removed changes to the Item_func_between::fix_length_and_dec() made in the fix for bug#16377
parent a33fdb44
...@@ -840,39 +840,36 @@ drop table t1; ...@@ -840,39 +840,36 @@ drop table t1;
create table t1(f1 date, f2 time, f3 datetime); create table t1(f1 date, f2 time, f3 datetime);
insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01");
insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02");
select f1 from t1 where f1 between "2006-1-1" and 20060101; select f1 from t1 where f1 between CAST("2006-1-1" as date) and CAST(20060101 as date);
f1 f1
2006-01-01 2006-01-01
select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; select f1 from t1 where f1 between cast("2006-1-1" as date) and cast("2006.1.1" as date);
f1 f1
2006-01-01 2006-01-01
select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; select f1 from t1 where date(f1) between cast("2006-1-1" as date) and cast("2006.1.1" as date);
f1 f1
2006-01-01 2006-01-01
select f2 from t1 where f2 between "12:1:2" and "12:2:2"; select f2 from t1 where f2 between cast("12:1:2" as time) and cast("12:2:2" as time);
f2 f2
12:01:02 12:01:02
select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; select f2 from t1 where time(f2) between cast("12:1:2" as time) and cast("12:2:2" as time);
f2 f2
12:01:02 12:01:02
select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; select f3 from t1 where f3 between cast("2006-1-1 12:1:1" as datetime) and cast("2006-1-1 12:1:2" as datetime);
f3 f3
2006-01-01 12:01:01 2006-01-01 12:01:01
select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; select f3 from t1 where timestamp(f3) between cast("2006-1-1 12:1:1" as datetime) and cast("2006-1-1 12:1:2" as datetime);
f3 f3
2006-01-01 12:01:01 2006-01-01 12:01:01
select f1 from t1 where "2006-1-1" between f1 and f3; select f1 from t1 where cast("2006-1-1" as date) between f1 and f3;
f1 f1
2006-01-01 2006-01-01
select f1 from t1 where "2006-1-1" between date(f1) and date(f3); select f1 from t1 where cast("2006-1-1" as date) between date(f1) and date(f3);
f1 f1
2006-01-01 2006-01-01
select f1 from t1 where "2006-1-1" between f1 and 'zzz'; select f1 from t1 where cast("2006-1-1" as date) between f1 and 'zzz';
f1 f1
Warnings: 2006-01-01
Warning 1292 Incorrect date value: 'zzz' for column 'f1' at row 1
Warning 1292 Truncated incorrect DOUBLE value: 'zzz'
Warning 1292 Truncated incorrect DOUBLE value: 'zzz'
select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); select f1 from t1 where makedate(2006,1) between date(f1) and date(f3);
f1 f1
2006-01-01 2006-01-01
......
...@@ -947,24 +947,24 @@ COUNT(*) ...@@ -947,24 +947,24 @@ COUNT(*)
Warnings: Warnings:
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050328 invalid'; SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050328 invalid';
COUNT(*) COUNT(*)
0 0
Warnings: Warnings:
Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1
Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1
Warning 1292 Truncated incorrect DOUBLE value: '20050328 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050328 invalid'
Warning 1292 Truncated incorrect DOUBLE value: '20050328 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050328 invalid'
SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050327 invalid'; SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050327 invalid';
COUNT(*) COUNT(*)
0 0
Warnings: Warnings:
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
show status like "Qcache_queries_in_cache"; show status like "Qcache_queries_in_cache";
Variable_name Value Variable_name Value
Qcache_queries_in_cache 0 Qcache_queries_in_cache 0
......
...@@ -27,12 +27,12 @@ INSERT INTO t1 VALUES ( "2000-1-2" ); ...@@ -27,12 +27,12 @@ INSERT INTO t1 VALUES ( "2000-1-2" );
INSERT INTO t1 VALUES ( "2000-1-3" ); INSERT INTO t1 VALUES ( "2000-1-3" );
INSERT INTO t1 VALUES ( "2000-1-4" ); INSERT INTO t1 VALUES ( "2000-1-4" );
INSERT INTO t1 VALUES ( "2000-1-5" ); INSERT INTO t1 VALUES ( "2000-1-5" );
SELECT * FROM t1 WHERE datum BETWEEN "2000-1-2" AND "2000-1-4"; SELECT * FROM t1 WHERE datum BETWEEN cast("2000-1-2" as date) AND cast("2000-1-4" as date);
datum datum
2000-01-02 2000-01-02
2000-01-03 2000-01-03
2000-01-04 2000-01-04
SELECT * FROM t1 WHERE datum BETWEEN "2000-1-2" AND datum - INTERVAL 100 DAY; SELECT * FROM t1 WHERE datum BETWEEN cast("2000-1-2" as date) AND datum - INTERVAL 100 DAY;
datum datum
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
...@@ -104,3 +104,9 @@ SELECT * FROM t1; ...@@ -104,3 +104,9 @@ SELECT * FROM t1;
y y
0000 0000
DROP TABLE t1; DROP TABLE t1;
create table t1(start_date date, end_date date);
insert into t1 values ('2000-01-01','2000-01-02');
select 1 from t1 where cast('2000-01-01 12:01:01' as datetime) between start_date and end_date;
1
1
drop table t1;
...@@ -2586,13 +2586,13 @@ INSERT INTO t1 VALUES ...@@ -2586,13 +2586,13 @@ INSERT INTO t1 VALUES
(4, '2005-01-03'), (5, '2005-01-04'), (6, '2005-01-05'), (4, '2005-01-03'), (5, '2005-01-04'), (6, '2005-01-05'),
(7, '2005-01-05'), (8, '2005-01-05'), (9, '2005-01-06'); (7, '2005-01-05'), (8, '2005-01-05'), (9, '2005-01-06');
CREATE VIEW v1 AS SELECT * FROM t1; CREATE VIEW v1 AS SELECT * FROM t1;
SELECT * FROM t1 WHERE td BETWEEN '2005.01.02' AND '2005.01.04'; SELECT * FROM t1 WHERE td BETWEEN CAST('2005.01.02' AS DATE) AND CAST('2005.01.04' AS DATE);
id td id td
2 2005-01-02 2 2005-01-02
3 2005-01-02 3 2005-01-02
4 2005-01-03 4 2005-01-03
5 2005-01-04 5 2005-01-04
SELECT * FROM v1 WHERE td BETWEEN '2005.01.02' AND '2005.01.04'; SELECT * FROM v1 WHERE td BETWEEN CAST('2005.01.02' AS DATE) AND CAST('2005.01.04' AS DATE);
id td id td
2 2005-01-02 2 2005-01-02
3 2005-01-02 3 2005-01-02
......
...@@ -419,20 +419,20 @@ drop table t1; ...@@ -419,20 +419,20 @@ drop table t1;
# #
# Bug#16377 result of DATE/TIME functions were compared as strings which # Bug#16377 result of DATE/TIME functions were compared as strings which
# can lead to a wrong result. # can lead to a wrong result.
# # Now wrong dates should be compared only with CAST()
create table t1(f1 date, f2 time, f3 datetime); create table t1(f1 date, f2 time, f3 datetime);
insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01");
insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02");
select f1 from t1 where f1 between "2006-1-1" and 20060101; select f1 from t1 where f1 between CAST("2006-1-1" as date) and CAST(20060101 as date);
select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; select f1 from t1 where f1 between cast("2006-1-1" as date) and cast("2006.1.1" as date);
select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; select f1 from t1 where date(f1) between cast("2006-1-1" as date) and cast("2006.1.1" as date);
select f2 from t1 where f2 between "12:1:2" and "12:2:2"; select f2 from t1 where f2 between cast("12:1:2" as time) and cast("12:2:2" as time);
select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; select f2 from t1 where time(f2) between cast("12:1:2" as time) and cast("12:2:2" as time);
select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; select f3 from t1 where f3 between cast("2006-1-1 12:1:1" as datetime) and cast("2006-1-1 12:1:2" as datetime);
select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; select f3 from t1 where timestamp(f3) between cast("2006-1-1 12:1:1" as datetime) and cast("2006-1-1 12:1:2" as datetime);
select f1 from t1 where "2006-1-1" between f1 and f3; select f1 from t1 where cast("2006-1-1" as date) between f1 and f3;
select f1 from t1 where "2006-1-1" between date(f1) and date(f3); select f1 from t1 where cast("2006-1-1" as date) between date(f1) and date(f3);
select f1 from t1 where "2006-1-1" between f1 and 'zzz'; select f1 from t1 where cast("2006-1-1" as date) between f1 and 'zzz';
select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); select f1 from t1 where makedate(2006,1) between date(f1) and date(f3);
select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); select f1 from t1 where makedate(2006,2) between date(f1) and date(f3);
drop table t1; drop table t1;
......
...@@ -36,8 +36,8 @@ INSERT INTO t1 VALUES ( "2000-1-2" ); ...@@ -36,8 +36,8 @@ INSERT INTO t1 VALUES ( "2000-1-2" );
INSERT INTO t1 VALUES ( "2000-1-3" ); INSERT INTO t1 VALUES ( "2000-1-3" );
INSERT INTO t1 VALUES ( "2000-1-4" ); INSERT INTO t1 VALUES ( "2000-1-4" );
INSERT INTO t1 VALUES ( "2000-1-5" ); INSERT INTO t1 VALUES ( "2000-1-5" );
SELECT * FROM t1 WHERE datum BETWEEN "2000-1-2" AND "2000-1-4"; SELECT * FROM t1 WHERE datum BETWEEN cast("2000-1-2" as date) AND cast("2000-1-4" as date);
SELECT * FROM t1 WHERE datum BETWEEN "2000-1-2" AND datum - INTERVAL 100 DAY; SELECT * FROM t1 WHERE datum BETWEEN cast("2000-1-2" as date) AND datum - INTERVAL 100 DAY;
DROP TABLE t1; DROP TABLE t1;
# #
...@@ -115,4 +115,11 @@ INSERT INTO t1 VALUES ('abc'); ...@@ -115,4 +115,11 @@ INSERT INTO t1 VALUES ('abc');
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
#
# Bug#21677: Wrong result when comparing a DATE and a DATETIME in BETWEEN
#
create table t1(start_date date, end_date date);
insert into t1 values ('2000-01-01','2000-01-02');
select 1 from t1 where cast('2000-01-01 12:01:01' as datetime) between start_date and end_date;
drop table t1;
# End of 4.1 tests # End of 4.1 tests
...@@ -2449,8 +2449,8 @@ INSERT INTO t1 VALUES ...@@ -2449,8 +2449,8 @@ INSERT INTO t1 VALUES
CREATE VIEW v1 AS SELECT * FROM t1; CREATE VIEW v1 AS SELECT * FROM t1;
SELECT * FROM t1 WHERE td BETWEEN '2005.01.02' AND '2005.01.04'; SELECT * FROM t1 WHERE td BETWEEN CAST('2005.01.02' AS DATE) AND CAST('2005.01.04' AS DATE);
SELECT * FROM v1 WHERE td BETWEEN '2005.01.02' AND '2005.01.04'; SELECT * FROM v1 WHERE td BETWEEN CAST('2005.01.02' AS DATE) AND CAST('2005.01.04' AS DATE);
DROP VIEW v1; DROP VIEW v1;
DROP TABLE t1; DROP TABLE t1;
......
...@@ -77,131 +77,14 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) ...@@ -77,131 +77,14 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems)
This function aggregates result types from the array of items. Found type This function aggregates result types from the array of items. Found type
supposed to be used later for comparison of values of these items. supposed to be used later for comparison of values of these items.
Aggregation itself is performed by the item_cmp_type() function. Aggregation itself is performed by the item_cmp_type() function.
NOTES
Aggregation rules:
If there are DATE/TIME fields/functions in the list and no string
fields/functions in the list then:
The INT_RESULT type will be used for aggregation instead of original
result type of any DATE/TIME field/function in the list
All constant items in the list will be converted to a DATE/TIME using
found field or result field of found function.
Implementation notes:
The code is equivalent to:
1. Check the list for presence of a STRING field/function.
Collect the is_const flag.
2. Get a Field* object to use for type coercion
3. Perform type conversion.
1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME
field/function and checks presence of a STRING field/function.
The second loop works only if a DATE/TIME field/function is found.
It checks presence of a STRING field/function in the rest of the list.
TODO
1) The current implementation can produce false comparison results for
expressions like:
date_time_field BETWEEN string_field_with_dates AND string_constant
if the string_constant will omit some of leading zeroes.
In order to fully implement correct comparison of DATE/TIME the new
DATETIME_RESULT result type should be introduced and agg_cmp_type()
should return the DATE/TIME field used for the conversion. Later
this field can be used by comparison functions like Item_func_between to
convert string values to ints on the fly and thus return correct results.
This modification will affect functions BETWEEN, IN and CASE.
2) If in the list a DATE field/function and a DATETIME field/function
are present in the list then the first found field/function will be
used for conversion. This may lead to wrong results and probably should
be fixed.
*/ */
static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems)
{ {
uint i; uint i;
Item::Type res= (Item::Type)0; type[0]= items[0]->result_type();
/* Used only for date/time fields, max_length = 19 */ for (i= 1 ; i < nitems ; i++)
char buff[20]; type[0]= item_cmp_type(type[0], items[i]->result_type());
uchar null_byte;
Field *field= NULL;
/*
Do not convert items while creating a or showing a view in order
to store/display the original query in these cases.
*/
if (thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
thd->lex->sql_command != SQLCOM_SHOW_CREATE)
{
/* Search for date/time fields/functions */
for (i= 0; i < nitems; i++)
{
if (!items[i]->result_as_longlong())
{
/* Do not convert anything if a string field/function is present */
if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT)
{
i= nitems;
break;
}
continue;
}
if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM &&
items[i]->result_type() != INT_RESULT)
{
field= ((Item_field *)items[i]->real_item())->field;
break;
}
else if (res == Item::FUNC_ITEM)
{
field= items[i]->tmp_table_field_from_field_type(0);
if (field)
field->move_field(buff, &null_byte, 0);
break;
}
}
}
if (field)
{
/* Check the rest of the list for presence of a string field/function. */
for (i++ ; i < nitems; i++)
{
if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT &&
!items[i]->result_as_longlong())
{
if (res == Item::FUNC_ITEM)
delete field;
field= 0;
break;
}
}
}
/*
If the first item is a date/time function then its result should be
compared as int
*/
if (field)
/* Suppose we are comparing dates */
type[0]= INT_RESULT;
else
type[0]= items[0]->result_type();
for (i= 0; i < nitems ; i++)
{
Item_result result= items[i]->result_type();
/*
Use INT_RESULT as result type for DATE/TIME fields/functions and
for constants successfully converted to DATE/TIME
*/
if (field &&
((!items[i]->const_item() && items[i]->result_as_longlong()) ||
(items[i]->const_item() && convert_constant_item(thd, field,
&items[i]))))
result= INT_RESULT;
type[0]= item_cmp_type(type[0], result);
}
if (res == Item::FUNC_ITEM && field)
delete field;
} }
...@@ -1222,8 +1105,32 @@ void Item_func_between::fix_length_and_dec() ...@@ -1222,8 +1105,32 @@ void Item_func_between::fix_length_and_dec()
agg_cmp_type(thd, &cmp_type, args, 3); agg_cmp_type(thd, &cmp_type, args, 3);
args[0]->cmp_context= args[1]->cmp_context= args[2]->cmp_context= cmp_type; args[0]->cmp_context= args[1]->cmp_context= args[2]->cmp_context= cmp_type;
if (cmp_type == STRING_RESULT) if (cmp_type == STRING_RESULT &&
agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1); agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1))
return;
/*
Make a special case of compare with date/time and longlong fields.
They are compared as integers, so for const item this time-consuming
conversion can be done only once, not for every single comparison
*/
if (args[0]->type() == FIELD_ITEM &&
thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
thd->lex->sql_command != SQLCOM_SHOW_CREATE)
{
Field *field=((Item_field*) args[0])->field;
if (field->can_be_compared_as_longlong())
{
/*
The following can't be recoded with || as convert_constant_item
changes the argument
*/
if (convert_constant_item(thd, field,&args[1]))
cmp_type=INT_RESULT; // Works for all types.
if (convert_constant_item(thd, field,&args[2]))
cmp_type=INT_RESULT; // Works for all types.
}
}
} }
......
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