Skip to content

Commit

Permalink
MDEV-15310 Range optimizer does not work well for "WHERE temporal_col…
Browse files Browse the repository at this point in the history
…umn NOT IN (const_list)"

There were two problems related to the bug report:
1. Item_datetime::get_date() was not implemented.
   So execution went through val_int() followed
   by int-to-datetime or int-to-time conversion.
   This was the reason why the optimizer did not
   work well on data with fractional seconds.
2. Item_datetime::set() did not have a TIME specific code
   to mix months and days to hours after unpack_time().
   This is why the optimizer did not work well with negative
   TIME values, as well as huge time values.

Changes:

1. Overriding Item_datetime::get_date(), to return ltime.
   This fixes the problem N1.
2. Cleanup: Moving pack_time() and unpack_time() from
   sql-common/my_time.c and include/my_time.h to
   sql/sql_time.cc and sql/sql_time.h, as they are not needed
   on the client side.
3. Adding a new "enum_mysql_timestamp_type ts_type" parameter
   to unpack_time() and moving the TIME specific code to mix
   months and days with hours inside unpack_time().
   Adding a new "ts_type" parameter to Item_datetime::set(),
   to pass it from the caller down to unpack_time().
   So now the TIME specific code is automatically called
   from Item_datetime::set(). This fixes the problem N2.
   This change also helped to get rid of duplicate TIME specific code
   from other three places, where mixing month/days to hours
   was done immediately after unpack_time().
   Moving the DATE specific code to zero hhmmssff
   from Item_func_min_max::get_date_native to inside unpack_time(),
   for symmetry.
4. Removing the virtual method in_vector::result_type(),
   adding in_vector::type_handler() instead.
   This helps to get result_type(), field_type(),
   mysql_timestamp_type() of an in_vector easier.
   Passing type_handler()->mysql_timestamp_type() as
   a new parameter to Item_datetime::set() inside
   in_temporal::value_to_item().
5. Cleaup: Removing separate implementations of in_datetime::get_value()
   and in_time::get_value(). Adding a single implementation
   in_temporal::get_value() instead.
   Passing type_handler()->field_type() to get_value_internal().
  • Loading branch information
Alexander Barkov committed Feb 14, 2018
1 parent 1fe9092 commit c17a06a
Show file tree
Hide file tree
Showing 15 changed files with 994 additions and 81 deletions.
3 changes: 0 additions & 3 deletions include/my_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *);
ulonglong TIME_to_ulonglong(const MYSQL_TIME *);
double TIME_to_double(const MYSQL_TIME *my_time);

longlong pack_time(const MYSQL_TIME *my_time);
MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time);

int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning);
my_bool check_datetime_range(const MYSQL_TIME *ltime);

Expand Down
81 changes: 81 additions & 0 deletions mysql-test/r/type_datetime.result
Original file line number Diff line number Diff line change
Expand Up @@ -1218,5 +1218,86 @@ a
DROP TABLE t1;
SET timestamp=DEFAULT;
#
# MDEV-15310 Range optimizer does not work well for "WHERE temporal_column NOT IN (const_list)"
#
#
# DATETIME(0)
#
CREATE TABLE t1 (a DATETIME, filler CHAR(200), KEY(a));
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:02', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:03', 'yes');
INSERT INTO t1 VALUES ('2001-01-01 23:00:04', 'yes');
EXPLAIN SELECT * FROM t1 WHERE a NOT IN ('2001-01-01 23:00:01','2001-01-01 23:00:02');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 6 NULL 5 Using index condition
SELECT * FROM t1 WHERE a NOT IN ('2001-01-01 23:00:01','2001-01-01 23:00:02');
a filler
2001-01-01 23:00:03 yes
2001-01-01 23:00:04 yes
DROP TABLE t1;
#
# DATETIME(1)
#
CREATE TABLE t1 (a DATETIME(1), filler CHAR(200), KEY(a));
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:01.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:02.1', 'no');
INSERT INTO t1 VALUES ('2001-01-01 23:00:03.1', 'yes');
INSERT INTO t1 VALUES ('2001-01-01 23:00:04.1', 'yes');
EXPLAIN SELECT * FROM t1 WHERE a NOT IN ('2001-01-01 23:00:01.1','2001-01-01 23:00:02.1');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 7 NULL 5 Using index condition
SELECT * FROM t1 WHERE a NOT IN ('2001-01-01 23:00:01.1','2001-01-01 23:00:02.1');
a filler
2001-01-01 23:00:03.1 yes
2001-01-01 23:00:04.1 yes
DROP TABLE t1;
#
# End of 10.3 tests
#
Loading

0 comments on commit c17a06a

Please sign in to comment.