Skip to content

Commit 316847e

Browse files
committed
MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result
TIMESTAMP columns were compared as strings in ALL/ANY comparison, which did not work well near DST time change. Changing ALL/ANY comparison to use "Native" representation to compare TIMESTAMP columns, like simple comparison does.
1 parent 36d173e commit 316847e

File tree

4 files changed

+95
-24
lines changed

4 files changed

+95
-24
lines changed

mysql-test/main/timezone2.result

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,3 +654,25 @@ SET time_zone=DEFAULT;
654654
#
655655
# End of 10.4 tests
656656
#
657+
#
658+
# MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result
659+
#
660+
SET time_zone='Europe/Moscow';
661+
CREATE TABLE t1 (a TIMESTAMP NULL);
662+
SET timestamp=1288477526;
663+
/* this is summer time, earlier */
664+
INSERT INTO t1 VALUES (NOW());
665+
SET timestamp=1288477526+3599;
666+
/* this is winter time, later */
667+
INSERT INTO t1 VALUES (NOW());
668+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
669+
a UNIX_TIMESTAMP(a)
670+
2010-10-31 02:25:26 1288477526
671+
2010-10-31 02:25:25 1288481125
672+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a <= ALL (SELECT * FROM t1);
673+
a UNIX_TIMESTAMP(a)
674+
2010-10-31 02:25:26 1288477526
675+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1);
676+
a UNIX_TIMESTAMP(a)
677+
2010-10-31 02:25:25 1288481125
678+
DROP TABLE t1;

mysql-test/main/timezone2.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,18 @@ SET time_zone=DEFAULT;
598598
--echo #
599599
--echo # End of 10.4 tests
600600
--echo #
601+
602+
--echo #
603+
--echo # MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result
604+
--echo #
605+
606+
SET time_zone='Europe/Moscow';
607+
CREATE TABLE t1 (a TIMESTAMP NULL);
608+
SET timestamp=1288477526; /* this is summer time, earlier */
609+
INSERT INTO t1 VALUES (NOW());
610+
SET timestamp=1288477526+3599; /* this is winter time, later */
611+
INSERT INTO t1 VALUES (NOW());
612+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
613+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a <= ALL (SELECT * FROM t1);
614+
SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1);
615+
DROP TABLE t1;

sql/sql_class.cc

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3698,6 +3698,41 @@ void select_max_min_finder_subselect::cleanup()
36983698
}
36993699

37003700

3701+
void select_max_min_finder_subselect::set_op(const Type_handler *th)
3702+
{
3703+
if (th->is_val_native_ready())
3704+
{
3705+
op= &select_max_min_finder_subselect::cmp_native;
3706+
return;
3707+
}
3708+
3709+
switch (th->cmp_type()) {
3710+
case REAL_RESULT:
3711+
op= &select_max_min_finder_subselect::cmp_real;
3712+
break;
3713+
case INT_RESULT:
3714+
op= &select_max_min_finder_subselect::cmp_int;
3715+
break;
3716+
case STRING_RESULT:
3717+
op= &select_max_min_finder_subselect::cmp_str;
3718+
break;
3719+
case DECIMAL_RESULT:
3720+
op= &select_max_min_finder_subselect::cmp_decimal;
3721+
break;
3722+
case TIME_RESULT:
3723+
if (th->field_type() == MYSQL_TYPE_TIME)
3724+
op= &select_max_min_finder_subselect::cmp_time;
3725+
else
3726+
op= &select_max_min_finder_subselect::cmp_str;
3727+
break;
3728+
case ROW_RESULT:
3729+
// This case should never be chosen
3730+
DBUG_ASSERT(0);
3731+
op= 0;
3732+
}
3733+
}
3734+
3735+
37013736
int select_max_min_finder_subselect::send_data(List<Item> &items)
37023737
{
37033738
DBUG_ENTER("select_max_min_finder_subselect::send_data");
@@ -3716,30 +3751,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items)
37163751
if (!cache)
37173752
{
37183753
cache= val_item->get_cache(thd);
3719-
switch (val_item->cmp_type()) {
3720-
case REAL_RESULT:
3721-
op= &select_max_min_finder_subselect::cmp_real;
3722-
break;
3723-
case INT_RESULT:
3724-
op= &select_max_min_finder_subselect::cmp_int;
3725-
break;
3726-
case STRING_RESULT:
3727-
op= &select_max_min_finder_subselect::cmp_str;
3728-
break;
3729-
case DECIMAL_RESULT:
3730-
op= &select_max_min_finder_subselect::cmp_decimal;
3731-
break;
3732-
case TIME_RESULT:
3733-
if (val_item->field_type() == MYSQL_TYPE_TIME)
3734-
op= &select_max_min_finder_subselect::cmp_time;
3735-
else
3736-
op= &select_max_min_finder_subselect::cmp_str;
3737-
break;
3738-
case ROW_RESULT:
3739-
// This case should never be choosen
3740-
DBUG_ASSERT(0);
3741-
op= 0;
3742-
}
3754+
set_op(val_item->type_handler());
37433755
}
37443756
cache->store(val_item);
37453757
it->store(0, cache);
@@ -3833,6 +3845,26 @@ bool select_max_min_finder_subselect::cmp_str()
38333845
return (sortcmp(val1, val2, cache->collation.collation) < 0);
38343846
}
38353847

3848+
3849+
bool select_max_min_finder_subselect::cmp_native()
3850+
{
3851+
NativeBuffer<STRING_BUFFER_USUAL_SIZE> cvalue, mvalue;
3852+
Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0);
3853+
bool cvalue_is_null= cache->val_native(thd, &cvalue);
3854+
bool mvalue_is_null= maxmin->val_native(thd, &mvalue);
3855+
3856+
/* Ignore NULLs for ANY and keep them for ALL subqueries */
3857+
if (cvalue_is_null)
3858+
return (is_all && !mvalue_is_null) || (!is_all && mvalue_is_null);
3859+
if (mvalue_is_null)
3860+
return !is_all;
3861+
3862+
const Type_handler *th= cache->type_handler();
3863+
return fmax ? th->cmp_native(cvalue, mvalue) > 0 :
3864+
th->cmp_native(cvalue, mvalue) < 0;
3865+
}
3866+
3867+
38363868
int select_exists_subselect::send_data(List<Item> &items)
38373869
{
38383870
DBUG_ENTER("select_exists_subselect::send_data");

sql/sql_class.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6068,6 +6068,7 @@ class select_max_min_finder_subselect :public select_subselect
60686068
bool (select_max_min_finder_subselect::*op)();
60696069
bool fmax;
60706070
bool is_all;
6071+
void set_op(const Type_handler *ha);
60716072
public:
60726073
select_max_min_finder_subselect(THD *thd_arg, Item_subselect *item_arg,
60736074
bool mx, bool all):
@@ -6080,6 +6081,7 @@ class select_max_min_finder_subselect :public select_subselect
60806081
bool cmp_decimal();
60816082
bool cmp_str();
60826083
bool cmp_time();
6084+
bool cmp_native();
60836085
};
60846086

60856087
/* EXISTS subselect interface class */

0 commit comments

Comments
 (0)