mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Merge epotemkin@bk-internal.mysql.com:/home/bk/mysql-4.1-opt
into moonbone.local:/work/21677-bug-4.1-opt-mysql
This commit is contained in:
@ -646,37 +646,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 Truncated incorrect date 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
|
||||||
|
@ -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;
|
|||||||
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;
|
||||||
|
@ -341,20 +341,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" );
|
|||||||
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');
|
|||||||
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
|
||||||
|
@ -75,119 +75,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;
|
|
||||||
|
|
||||||
/* 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();
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
|
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
|
||||||
@ -1021,8 +916,30 @@ void Item_func_between::fix_length_and_dec()
|
|||||||
if (!args[0] || !args[1] || !args[2])
|
if (!args[0] || !args[1] || !args[2])
|
||||||
return;
|
return;
|
||||||
agg_cmp_type(thd, &cmp_type, args, 3);
|
agg_cmp_type(thd, &cmp_type, args, 3);
|
||||||
if (cmp_type == STRING_RESULT)
|
if (cmp_type == STRING_RESULT &&
|
||||||
agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV);
|
agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV))
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user