diff options
author | Oleg Smirnov <olernov@gmail.com> | 2023-02-17 19:21:30 +0700 |
---|---|---|
committer | Oleg Smirnov <olernov@gmail.com> | 2023-04-25 20:21:35 +0700 |
commit | f0b665f880bb6a6864660818d926fde9db18fa3e (patch) | |
tree | 98c94847fde17e9d25780d65075191547cf61812 | |
parent | 54c11273e340620f319d0675166bd04b6e64db26 (diff) | |
download | mariadb-git-f0b665f880bb6a6864660818d926fde9db18fa3e.tar.gz |
MDEV-8320 Allow index usage for DATE(col) <=> const and YEAR <=> const
Rewrite datetime comparison conditions into sargeable. For example,
YEAR(col) <= val -> col <= YEAR_END(val)
YEAR(col) < val -> col < YEAR_START(val)
YEAR(col) >= val -> col >= YEAR_START(val)
YEAR(col) > val -> col > YEAR_END(val)
YEAR(col) = val -> col BETWEEN YEAR_START(val) AND YEAR_END(val)
Do the same with DATE(col), for example:
DATE(col) <= val -> col <= DAY_END(val)
After such a rewrite index lookup on column "col" can be employed
-rw-r--r-- | libmysqld/CMakeLists.txt | 4 | ||||
-rw-r--r-- | mysql-test/main/sargable_date_cond.result | 2328 | ||||
-rw-r--r-- | mysql-test/main/sargable_date_cond.test | 539 | ||||
-rw-r--r-- | sql/CMakeLists.txt | 4 | ||||
-rw-r--r-- | sql/item.cc | 13 | ||||
-rw-r--r-- | sql/item.h | 6 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 1 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 13 | ||||
-rw-r--r-- | sql/item_func.cc | 2 | ||||
-rw-r--r-- | sql/item_func.h | 1 | ||||
-rw-r--r-- | sql/item_timefunc.h | 4 | ||||
-rw-r--r-- | sql/opt_rewrite_date_cmp.cc | 363 | ||||
-rw-r--r-- | sql/opt_rewrite_date_cmp.h | 111 | ||||
-rw-r--r-- | sql/sql_lex.h | 20 | ||||
-rw-r--r-- | sql/sql_select.cc | 16 | ||||
-rw-r--r-- | sql/sql_select.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 3 |
17 files changed, 3421 insertions, 8 deletions
diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 5d5cc35e1be..fb27cb221c1 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -68,7 +68,9 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/key.cc ../sql/lock.cc ../sql/log.cc ../sql/log_event.cc ../sql/log_event_server.cc ../sql/mf_iocache.cc ../sql/my_decimal.cc - ../sql/net_serv.cc ../sql/opt_range.cc ../sql/opt_sum.cc + ../sql/net_serv.cc ../sql/opt_range.cc + ../sql/opt_rewrite_date_cmp.cc + ../sql/opt_sum.cc ../sql/parse_file.cc ../sql/procedure.cc ../sql/protocol.cc ../sql/records.cc ../sql/repl_failsafe.cc ../sql/rpl_filter.cc ../sql/rpl_record.cc ../sql/des_key_file.cc diff --git a/mysql-test/main/sargable_date_cond.result b/mysql-test/main/sargable_date_cond.result new file mode 100644 index 00000000000..5c0ac7cd16d --- /dev/null +++ b/mysql-test/main/sargable_date_cond.result @@ -0,0 +1,2328 @@ +drop table if exists t0,t1,t2,t3; +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 select A.a + B.a* 10 from t0 A, t0 B; +create table t2 (pk int primary key, a datetime, b date, key(a), key(b)); +insert into t2 +select +A.a*10+B.a, +date_add(date_add('2017-01-01', interval A.a*8 day), interval B.a hour), +date_add('2017-01-01', interval A.a*7 day) +from t1 A, t0 B; +# +# "YEAR(datetime_col) CMP year_value", basic checks +# +select count(*) from t2 where year(a) < 2018; +count(*) +460 +select count(*) from t2 where a < '2018-01-01'; +count(*) +460 +explain format=json select * from t2 force index(a) where year(a) < 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a < '2018-01-01 00:00:00'" + } + } + ] + } +} +# Check rewrite for a prepared statement: +execute immediate +"explain format=json select * from t2 force index(a) where year(a) < ?" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a < '2018-01-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where year(a) < 2018"; +execute stmt; +count(*) +460 +execute stmt; +count(*) +460 +create or replace view v1 as select count(*) from t2 where year(a) < 2018; +select * from v1; +count(*) +460 +create or replace procedure sp() select count(*) from t2 where year(a) < 2018; +call sp(); +count(*) +460 +call sp(); +count(*) +460 +# Prepared statement with a placeholder +prepare stmt from "select count(*) from t2 where year(a) < ?"; +execute stmt using 2018; +count(*) +460 +execute stmt using 2017; +count(*) +0 +select count(*) from t2 where year(a) <= 2018; +count(*) +920 +select count(*) from t2 where a < '2019-01-01'; +count(*) +920 +explain format=json select * from t2 force index(a) where year(a) <= 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.115764791, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 903, + "cost": 1.115764791, + "filtered": 100, + "index_condition": "t2.a <= '2018-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where year(a) <= ?" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.115764791, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 903, + "cost": 1.115764791, + "filtered": 100, + "index_condition": "t2.a <= '2018-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where year(a) <= 2018"; +execute stmt; +count(*) +920 +execute stmt; +count(*) +920 +create or replace view v1 as select count(*) from t2 where year(a) <= 2018; +select * from v1; +count(*) +920 +create or replace procedure sp() select count(*) from t2 where year(a) <= 2018; +call sp(); +count(*) +920 +call sp(); +count(*) +920 +prepare stmt from "select count(*) from t2 where year(a) <= ?"; +execute stmt using 2018; +count(*) +920 +execute stmt using 2017; +count(*) +460 +select count(*) from t2 where year(a) > 2018; +count(*) +80 +select count(*) from t2 where a > '2018-12-31 23:59:59.999999'; +count(*) +80 +explain format=json select * from t2 force index(a) where year(a) > 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.121025977, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 97, + "cost": 0.121025977, + "filtered": 100, + "index_condition": "t2.a > '2018-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where year(a) > ?" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.121025977, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 97, + "cost": 0.121025977, + "filtered": 100, + "index_condition": "t2.a > '2018-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where year(a) > 2018"; +execute stmt; +count(*) +80 +execute stmt; +count(*) +80 +create or replace view v1 as select count(*) from t2 where year(a) > 2018; +select * from v1; +count(*) +80 +create or replace procedure sp() select count(*) from t2 where year(a) > 2018; +call sp(); +count(*) +80 +call sp(); +count(*) +80 +prepare stmt from "select count(*) from t2 where year(a) > ?"; +execute stmt using 2018; +count(*) +80 +execute stmt using 2017; +count(*) +540 +select count(*) from t2 where year(a) >= 2018; +count(*) +540 +select count(*) from t2 where a >= '2018-01-01'; +count(*) +540 +explain format=json select * from t2 force index(a) where year(a) >= 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a >= '2018-01-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where year(a) >= ?" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a >= '2018-01-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where year(a) >= 2018"; +execute stmt; +count(*) +540 +execute stmt; +count(*) +540 +create or replace view v1 as select count(*) from t2 where year(a) >= 2018; +select * from v1; +count(*) +540 +create or replace procedure sp() select count(*) from t2 where year(a) >= 2018; +call sp(); +count(*) +540 +call sp(); +count(*) +540 +prepare stmt from "select count(*) from t2 where year(a) >= ?"; +execute stmt using 2018; +count(*) +540 +execute stmt using 2019; +count(*) +80 +select count(*) from t2 where year(a) = 2017; +count(*) +460 +select count(*) from t2 where a >= '2017-01-01' and a < '2018-01-01'; +count(*) +460 +explain format=json select * from t2 force index(a) where year(a) = 2017; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a between '2017-01-01 00:00:00' and '2017-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where year(a) = ?" + using 2017; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a between '2017-01-01 00:00:00' and '2017-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where year(a) = 2017"; +execute stmt; +count(*) +460 +execute stmt; +count(*) +460 +create or replace view v1 as select count(*) from t2 where year(a) = 2017; +select * from v1; +count(*) +460 +create or replace procedure sp() select count(*) from t2 where year(a) = 2017; +call sp(); +count(*) +460 +call sp(); +count(*) +460 +prepare stmt from "select count(*) from t2 where year(a) = ?"; +execute stmt using 2017; +count(*) +460 +execute stmt using 2019; +count(*) +80 +# +# "YEAR(datetime_col) CMP year_value", reverse argument order +# +select count(*) from t2 where 2017 < year(a); +count(*) +540 +select count(*) from t2 where a >= '2018-01-01'; +count(*) +540 +explain format=json select * from t2 force index(a) where 2017 < year(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a > '2017-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? < year(a)" + using 2017; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a > '2017-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where 2017 < year(a)"; +execute stmt; +count(*) +540 +execute stmt; +count(*) +540 +create or replace view v1 as select count(*) from t2 where 2017 < year(a); +select * from v1; +count(*) +540 +create or replace procedure sp() select count(*) from t2 where 2017 < year(a); +call sp(); +count(*) +540 +call sp(); +count(*) +540 +prepare stmt from "select count(*) from t2 where ? < year(a)"; +execute stmt using 2017; +count(*) +540 +execute stmt using 2018; +count(*) +80 +select count(*) from t2 where a >= '2018-01-01'; +count(*) +540 +explain format=json select * from t2 force index(a) where 2018 <= year(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a >= '2018-01-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? <= year(a)" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.678734765, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 549, + "cost": 0.678734765, + "filtered": 100, + "index_condition": "t2.a >= '2018-01-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where 2018 <= year(a)"; +execute stmt; +count(*) +540 +execute stmt; +count(*) +540 +create or replace view v1 as select count(*) from t2 where 2018 <= year(a); +select * from v1; +count(*) +540 +create or replace procedure sp() select count(*) from t2 where 2018 <= year(a); +call sp(); +count(*) +540 +call sp(); +count(*) +540 +prepare stmt from "select count(*) from t2 where ? <= year(a)"; +execute stmt using 2018; +count(*) +540 +execute stmt using 2019; +count(*) +80 +select count(*) from t2 where 2018 > year(a); +count(*) +460 +select count(*) from t2 where a < '2018-01-01'; +count(*) +460 +explain format=json select * from t2 force index(a) where 2018 > year(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a < '2018-01-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? > year(a)" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.558056003, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t2.a < '2018-01-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where 2018 > year(a)"; +execute stmt; +count(*) +460 +execute stmt; +count(*) +460 +create or replace view v1 as select count(*) from t2 where 2018 > year(a); +select * from v1; +count(*) +460 +create or replace procedure sp() select count(*) from t2 where 2018 > year(a); +call sp(); +count(*) +460 +call sp(); +count(*) +460 +prepare stmt from "select count(*) from t2 where ? > year(a)"; +execute stmt using 2018; +count(*) +460 +execute stmt using 2019; +count(*) +920 +select count(*) from t2 where a < '2019-01-01'; +count(*) +920 +explain format=json select * from t2 force index(a) where 2018 >= year(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.115764791, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 903, + "cost": 1.115764791, + "filtered": 100, + "index_condition": "t2.a <= '2018-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? >= year(a)" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.115764791, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 903, + "cost": 1.115764791, + "filtered": 100, + "index_condition": "t2.a <= '2018-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where 2018 >= year(a)"; +execute stmt; +count(*) +920 +execute stmt; +count(*) +920 +create or replace view v1 as select count(*) from t2 where 2018 >= year(a); +select * from v1; +count(*) +920 +create or replace procedure sp() select count(*) from t2 where 2018 >= year(a); +call sp(); +count(*) +920 +call sp(); +count(*) +920 +prepare stmt from "select count(*) from t2 where ? >= year(a)"; +execute stmt using 2018; +count(*) +920 +execute stmt using 2019; +count(*) +1000 +select count(*) from t2 where 2018 = year(a); +count(*) +460 +select count(*) from t2 where a >= '2018-01-01' and a < '2019-01-01'; +count(*) +460 +explain format=json select * from t2 force index(a) where 2018 = year(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.559289872, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 452, + "cost": 0.559289872, + "filtered": 100, + "index_condition": "t2.a between '2018-01-01 00:00:00' and '2018-12-31 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? = year(a)" + using 2018; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.559289872, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 452, + "cost": 0.559289872, + "filtered": 100, + "index_condition": "t2.a between '2018-01-01 00:00:00' and '2018-12-31 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where 2018 = year(a)"; +execute stmt; +count(*) +460 +execute stmt; +count(*) +460 +create or replace view v1 as select count(*) from t2 where 2018 = year(a); +select * from v1; +count(*) +460 +create or replace procedure sp() select count(*) from t2 where 2018 = year(a); +call sp(); +count(*) +460 +call sp(); +count(*) +460 +prepare stmt from "select count(*) from t2 where ? = year(a)"; +execute stmt using 2018; +count(*) +460 +execute stmt using 2019; +count(*) +80 +# +# "DATE(datetime_col) CMP date_value", basic checks +# +select count(*) from t2 where date(a) < '2017-06-01'; +count(*) +190 +select count(*) from t2 where a < '2017-06-01'; +count(*) +190 +explain format=json select * from t2 force index(a) where date(a)< '2017-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.230840318, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 186, + "cost": 0.230840318, + "filtered": 100, + "index_condition": "t2.a < '2017-06-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where date(a) < ?" + using '2017-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.230840318, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 186, + "cost": 0.230840318, + "filtered": 100, + "index_condition": "t2.a < '2017-06-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(a) < '2017-06-01'"; +execute stmt; +count(*) +190 +execute stmt; +count(*) +190 +create or replace view v1 as select count(*) from t2 where date(a) < '2017-06-01'; +select * from v1; +count(*) +190 +create or replace procedure sp() select count(*) from t2 where date(a) < '2017-06-01'; +call sp(); +count(*) +190 +call sp(); +count(*) +190 +prepare stmt from "select count(*) from t2 where date(a) < ?"; +execute stmt using '2017-06-01'; +count(*) +190 +execute stmt using '2017-06-05'; +count(*) +200 +select count(*) from t2 where date(a) <= '2017-06-03'; +count(*) +200 +select count(*) from t2 where a < '2017-06-04'; +count(*) +200 +explain format=json select * from t2 force index(a) where date(a)<='2017-06-04'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.243179008, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 196, + "cost": 0.243179008, + "filtered": 100, + "index_condition": "t2.a <= '2017-06-04 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where date(a) <= ?" + using '2017-06-04'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.243179008, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 196, + "cost": 0.243179008, + "filtered": 100, + "index_condition": "t2.a <= '2017-06-04 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(a) <= '2017-06-03'"; +execute stmt; +count(*) +200 +execute stmt; +count(*) +200 +create or replace view v1 as select count(*) from t2 where date(a) <= '2017-06-03'; +select * from v1; +count(*) +200 +create or replace procedure sp() select count(*) from t2 where date(a) <= '2017-06-03'; +call sp(); +count(*) +200 +call sp(); +count(*) +200 +prepare stmt from "select count(*) from t2 where date(a) <= ?"; +execute stmt using '2017-06-03'; +count(*) +200 +execute stmt using '2017-06-10'; +count(*) +210 +select count(*) from t2 where date(a) > '2018-06-01'; +count(*) +350 +select count(*) from t2 where a >= '2018-06-02'; +count(*) +350 +explain format=json select * from t2 force index(a) where date(a)> '2018-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a > '2018-06-01 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where date(a) > ?" + using '2018-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a > '2018-06-01 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(a) > '2018-06-01'"; +execute stmt; +count(*) +350 +execute stmt; +count(*) +350 +create or replace view v1 as select count(*) from t2 where date(a) > '2018-06-01'; +select * from v1; +count(*) +350 +create or replace procedure sp() select count(*) from t2 where date(a) > '2018-06-01'; +call sp(); +count(*) +350 +call sp(); +count(*) +350 +prepare stmt from "select count(*) from t2 where date(a) > ?"; +execute stmt using '2018-06-01'; +count(*) +350 +execute stmt using '2018-06-05'; +count(*) +340 +select count(*) from t2 where date(a) >= '2018-06-01'; +count(*) +350 +select count(*) from t2 where a >= '2018-06-01'; +count(*) +350 +explain format=json select * from t2 force index(a) where date(a)>='2018-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a >= '2018-06-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where date(a) >= ?" + using '2018-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a >= '2018-06-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(a) >= '2018-06-01'"; +execute stmt; +count(*) +350 +execute stmt; +count(*) +350 +create or replace view v1 as select count(*) from t2 where date(a) >= '2018-06-01'; +select * from v1; +count(*) +350 +create or replace procedure sp() select count(*) from t2 where date(a) >= '2018-06-01'; +call sp(); +count(*) +350 +call sp(); +count(*) +350 +prepare stmt from "select count(*) from t2 where date(a) >= ?"; +execute stmt using '2018-06-01'; +count(*) +350 +execute stmt using '2018-06-10'; +count(*) +340 +select count(*) from t2 where date(a) = '2017-06-02'; +count(*) +10 +select count(*) from t2 where a >= '2017-06-02' and a < '2017-06-03'; +count(*) +10 +explain format=json select * from t2 force index(a) where date(a)= '2017-06-02'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.013679374, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 10, + "cost": 0.013679374, + "filtered": 100, + "index_condition": "t2.a between '2017-06-02 00:00:00' and '2017-06-02 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where date(a) = ?" + using '2017-06-02'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.013679374, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 10, + "cost": 0.013679374, + "filtered": 100, + "index_condition": "t2.a between '2017-06-02 00:00:00' and '2017-06-02 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(a) = '2017-06-02'"; +execute stmt; +count(*) +10 +execute stmt; +count(*) +10 +create or replace view v1 as select count(*) from t2 where date(a) = '2017-06-02'; +select * from v1; +count(*) +10 +create or replace procedure sp() select count(*) from t2 where date(a) = '2017-06-02'; +call sp(); +count(*) +10 +call sp(); +count(*) +10 +prepare stmt from "select count(*) from t2 where date(a) = ?"; +execute stmt using '2017-06-02'; +count(*) +10 +execute stmt using '2017-06-05'; +count(*) +0 +# +# "DATE(datetime_col) CMP date_value", reverse order +# +select count(*) from t2 where '2017-06-01' > date(a); +count(*) +190 +select count(*) from t2 where '2017-06-01' > a; +count(*) +190 +explain format=json select * from t2 force index(a) where '2017-06-01' > date(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.230840318, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 186, + "cost": 0.230840318, + "filtered": 100, + "index_condition": "t2.a < '2017-06-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? > date(a)" + using '2017-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.230840318, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 186, + "cost": 0.230840318, + "filtered": 100, + "index_condition": "t2.a < '2017-06-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where '2017-06-01' > date(a)"; +execute stmt; +count(*) +190 +execute stmt; +count(*) +190 +create or replace view v1 as select count(*) from t2 where '2017-06-01' > date(a); +select * from v1; +count(*) +190 +create or replace procedure sp() select count(*) from t2 where '2017-06-01' > date(a); +call sp(); +count(*) +190 +call sp(); +count(*) +190 +prepare stmt from "select count(*) from t2 where ? > date(a)"; +execute stmt using '2017-06-01'; +count(*) +190 +execute stmt using '2017-06-05'; +count(*) +200 +select count(*) from t2 where '2017-06-03' >= date(a); +count(*) +200 +select count(*) from t2 where '2017-06-03' >= a; +count(*) +200 +explain format=json select * from t2 force index(a) where '2017-06-03' >= date(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.243179008, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 196, + "cost": 0.243179008, + "filtered": 100, + "index_condition": "t2.a <= '2017-06-03 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? >= date(a)" + using '2017-06-03'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.243179008, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 196, + "cost": 0.243179008, + "filtered": 100, + "index_condition": "t2.a <= '2017-06-03 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where '2017-06-03' >= date(a)"; +execute stmt; +count(*) +200 +execute stmt; +count(*) +200 +create or replace view v1 as select count(*) from t2 where '2017-06-03' >= date(a); +select * from v1; +count(*) +200 +create or replace procedure sp() select count(*) from t2 where '2017-06-03' >= date(a); +call sp(); +count(*) +200 +call sp(); +count(*) +200 +prepare stmt from "select count(*) from t2 where ? >= date(a)"; +execute stmt using '2017-06-03'; +count(*) +200 +execute stmt using '2017-06-12'; +count(*) +210 +select count(*) from t2 where '2018-06-01' < date(a); +count(*) +350 +select count(*) from t2 where '2018-06-02' <= a; +count(*) +350 +explain format=json select * from t2 force index(a) where '2018-06-01' < date(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a > '2018-06-01 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? < date(a)" + using '2017-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.00595045, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 814, + "cost": 1.00595045, + "filtered": 100, + "index_condition": "t2.a > '2017-06-01 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where '2018-06-01' < date(a)"; +execute stmt; +count(*) +350 +execute stmt; +count(*) +350 +create or replace view v1 as select count(*) from t2 where '2018-06-01' < date(a); +select * from v1; +count(*) +350 +create or replace procedure sp() select count(*) from t2 where '2018-06-01' < date(a); +call sp(); +count(*) +350 +call sp(); +count(*) +350 +prepare stmt from "select count(*) from t2 where ? < date(a)"; +execute stmt using '2018-06-02'; +count(*) +350 +execute stmt using '2018-06-15'; +count(*) +330 +select count(*) from t2 where '2018-06-01' <= date(a); +count(*) +350 +select count(*) from t2 where '2018-06-01' <= a; +count(*) +350 +explain format=json select * from t2 force index(a) where '2018-06-01' <= date(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.449475531, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 363, + "cost": 0.449475531, + "filtered": 100, + "index_condition": "t2.a >= '2018-06-01 00:00:00'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? <= date(a)" + using '2017-06-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 1.00595045, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 814, + "cost": 1.00595045, + "filtered": 100, + "index_condition": "t2.a >= '2017-06-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where '2018-06-01' <= date(a)"; +execute stmt; +count(*) +350 +execute stmt; +count(*) +350 +create or replace view v1 as select count(*) from t2 where '2018-06-01' <= date(a); +select * from v1; +count(*) +350 +create or replace procedure sp() select count(*) from t2 where '2018-06-01' <= date(a); +call sp(); +count(*) +350 +call sp(); +count(*) +350 +prepare stmt from "select count(*) from t2 where ? <= date(a)"; +execute stmt using '2018-06-01'; +count(*) +350 +execute stmt using '2018-06-15'; +count(*) +330 +select count(*) from t2 where '2017-06-02' = date(a); +count(*) +10 +select count(*) from t2 where a >= '2017-06-02' and a < '2017-06-03'; +count(*) +10 +explain format=json select * from t2 force index(a) where '2017-06-02' = date(a); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.013679374, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 10, + "cost": 0.013679374, + "filtered": 100, + "index_condition": "t2.a between '2017-06-02 00:00:00' and '2017-06-02 23:59:59'" + } + } + ] + } +} +execute immediate +"explain format=json select * from t2 force index(a) where ? = date(a)" + using '2017-06-02'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.013679374, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 10, + "cost": 0.013679374, + "filtered": 100, + "index_condition": "t2.a between '2017-06-02 00:00:00' and '2017-06-02 23:59:59'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where '2017-06-02' = date(a)"; +execute stmt; +count(*) +10 +execute stmt; +count(*) +10 +create or replace view v1 as select count(*) from t2 where '2017-06-02' = date(a); +select * from v1; +count(*) +10 +create or replace procedure sp() select count(*) from t2 where '2017-06-02' = date(a); +call sp(); +count(*) +10 +call sp(); +count(*) +10 +prepare stmt from "select count(*) from t2 where ? = date(a)"; +execute stmt using '2017-06-03'; +count(*) +0 +execute stmt using '2017-06-10'; +count(*) +10 +# Check rewrite of a more complicated query +explain format=json select * from t2 as t21 force index(a), +t2 as t22 force index(a) +where year(t21.a) < 2018 and t21.b > '2017-11-01' + and date(t22.a) >= '2017-02-01' and t22.b > '2017-11-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 536.1519424, + "nested_loop": [ + { + "table": { + "table_name": "t21", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 451, + "cost": 0.558056003, + "filtered": 100, + "index_condition": "t21.a < '2018-01-01 00:00:00'", + "attached_condition": "t21.b > '2017-11-01'" + } + }, + { + "block-nl-join": { + "table": { + "table_name": "t22", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 451, + "rows": 961, + "cost": 535.5938864, + "filtered": 100, + "index_condition": "t22.a >= '2017-02-01 00:00:00'", + "attached_condition": "t22.a >= '2017-02-01 00:00:00' and t22.b > '2017-11-01'" + }, + "buffer_type": "flat", + "buffer_size": "7Kb", + "join_type": "BNL", + "attached_condition": "t22.b > '2017-11-01'" + } + } + ] + } +} +# +# Incorrect const values processing (no rewrite is possible) +# +explain format=json select * from t2 where year(a) = -1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "year(t2.a) = -1" + } + } + ] + } +} +explain format=json select * from t2 where year(a) > -5; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "year(t2.a) > -5" + } + } + ] + } +} +explain format=json select * from t2 where year(a) < -1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "year(t2.a) < -1" + } + } + ] + } +} +explain format=json select * from t2 where year(a) <= 10000; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "year(t2.a) <= 10000" + } + } + ] + } +} +explain format=json select * from t2 where year(a) >= 10020; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "year(t2.a) >= 10020" + } + } + ] + } +} +explain format=json select * from t2 where date(a) = '10000-01-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "cast(t2.a as date) = '10000-01-01'" + } + } + ] + } +} +Warnings: +Warning 1292 Incorrect datetime value: '10000-01-01' +explain format=json select * from t2 where date(a) < '-1-01-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.1671618, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1000, + "cost": 0.1671618, + "filtered": 100, + "attached_condition": "cast(t2.a as date) < '-1-01-01'" + } + } + ] + } +} +Warnings: +Warning 1292 Incorrect datetime value: '-1-01-01' +# +# Try DATE function and DATE (not DATETIME) column: +# +select count(*) from t2 where date(b)< '2017-06-03'; +count(*) +220 +select count(*) from t2 where b < '2017-06-03'; +count(*) +220 +explain format=json select * from t2 force index(b) where date(b)< '2017-06-03'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.267856388, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["b"], + "key": "b", + "key_length": "4", + "used_key_parts": ["b"], + "loops": 1, + "rows": 216, + "cost": 0.267856388, + "filtered": 100, + "index_condition": "t2.b < '2017-06-03'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 where date(b)< '2017-06-03'"; +execute stmt; +count(*) +220 +execute stmt; +count(*) +220 +create or replace view v1 as select count(*) from t2 where date(b)< '2017-06-03'; +select * from v1; +count(*) +220 +create or replace procedure sp() select count(*) from t2 where date(b)< '2017-06-03'; +call sp(); +count(*) +220 +call sp(); +count(*) +220 +prepare stmt from "select count(*) from t2 where date(b) < ?"; +execute stmt using '2017-06-03'; +count(*) +220 +execute stmt using '2017-06-10'; +count(*) +230 +select count(*) from t2 force index(b) where date(b)= '2017-06-04'; +count(*) +10 +select count(*) from t2 where b >= '2017-06-04' and b < '2017-06-05'; +count(*) +10 +explain format=json select * from t2 force index(b) where date(b)='2017-06-04'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.013679374, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["b"], + "key": "b", + "key_length": "4", + "used_key_parts": ["b"], + "loops": 1, + "rows": 10, + "cost": 0.013679374, + "filtered": 100, + "index_condition": "t2.b between '2017-06-04' and '2017-06-04'" + } + } + ] + } +} +prepare stmt from "select count(*) from t2 force index(b) where date(b)= '2017-06-04'"; +execute stmt; +count(*) +10 +execute stmt; +count(*) +10 +create or replace view v1 as select count(*) from t2 force index(b) where date(b)= '2017-06-04'; +select * from v1; +count(*) +10 +create or replace procedure sp() select count(*) from t2 force index(b) where date(b)= '2017-06-04'; +call sp(); +count(*) +10 +call sp(); +count(*) +10 +prepare stmt from "select count(*) from t2 where date(b) = ?"; +execute stmt using '2017-06-04'; +count(*) +10 +execute stmt using '2017-06-10'; +count(*) +0 +# +# Check actual query results +# +insert into t2 values (10001,'2006-12-31 23:59:59','2006-12-31'); +insert into t2 values (10002,'2007-01-01 00:00:00','2007-01-01'); +insert into t2 values (10003,'2007-12-31 23:59:59','2007-12-31'); +insert into t2 values (10004,'2008-01-01 00:00:00','2008-01-01'); +explain format=json +select * from t2 force index(b) where year(b)=2007; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.005042291, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["b"], + "key": "b", + "key_length": "4", + "used_key_parts": ["b"], + "loops": 1, + "rows": 3, + "cost": 0.005042291, + "filtered": 100, + "index_condition": "t2.b between '2007-01-01' and '2007-12-31'" + } + } + ] + } +} +select * from t2 force index(b) where year(b)=2007; +pk a b +10002 2007-01-01 00:00:00 2007-01-01 +10003 2007-12-31 23:59:59 2007-12-31 +insert into t2 values (10010,'2006-12-31 00:00:00','2006-12-31'); +insert into t2 values (10011,'2006-12-30 23:59:59','2006-12-30'); +explain format=json +select * from t2 force index(a) where date(a)='2006-12-31'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.005042291, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "6", + "used_key_parts": ["a"], + "loops": 1, + "rows": 3, + "cost": 0.005042291, + "filtered": 100, + "index_condition": "t2.a between '2006-12-31 00:00:00' and '2006-12-31 23:59:59'" + } + } + ] + } +} +select * from t2 force index(a) where date(a)='2006-12-31'; +pk a b +10010 2006-12-31 00:00:00 2006-12-31 +10001 2006-12-31 23:59:59 2006-12-31 +# +# Test the TIMESTAMP column +# +create table t3 (a timestamp, b date, key(a)); +set time_zone="UTC"; +insert into t3 +select +timestampadd(hour, B.a, date_add('2016-01-01', interval A.a*8 day)), +date_add('2016-01-01', interval A.a*7 day) +from t1 A, t0 B; +# Results of those two queries must be equal: +select count(*) from t3 force index(a) where year(a)= 2016; +count(*) +460 +# The result must be the same as this query's: +select count(*) from t3 force index(a) where a >= '2016-01-01 00:00:00' + and a <= '2016-12-31 23:59:59.999999'; +count(*) +460 +explain format=json +select count(*) from t3 force index(a) where year(a)= 2016; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.064493934, + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "5", + "used_key_parts": ["a"], + "loops": 1, + "rows": 455, + "cost": 0.064493934, + "filtered": 100, + "attached_condition": "t3.a between '2016-01-01 00:00:00' and '2016-12-31 23:59:59'", + "using_index": true + } + } + ] + } +} +# Check rewrite for a prepared statement: +execute immediate +"explain format=json select * from t3 force index(a) where year(a) < ?" + using 2017; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.562991479, + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "range", + "possible_keys": ["a"], + "key": "a", + "key_length": "5", + "used_key_parts": ["a"], + "loops": 1, + "rows": 455, + "cost": 0.562991479, + "filtered": 100, + "index_condition": "t3.a < '2017-01-01 00:00:00'" + } + } + ] + } +} +prepare stmt from "select count(*) from t3 force index(a) where year(a)= 2016"; +execute stmt; +count(*) +460 +execute stmt; +count(*) +460 +create or replace view v1 as select count(*) from t3 force index(a) where year(a)= 2016; +select * from v1; +count(*) +460 +create or replace procedure sp() select count(*) from t3 force index(a) where year(a)= 2016; +call sp(); +count(*) +460 +call sp(); +count(*) +460 +# Prepared statement with a placeholder +prepare stmt from "select count(*) from t3 where year(a) < ?"; +execute stmt using 2017; +count(*) +460 +execute stmt using 2018; +count(*) +920 +set time_zone= @@global.time_zone; +# +# Incorrect const values processing (no rewrite is possible) +# +explain format=json select * from t2 where year(a) = -1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "year(t2.a) = -1" + } + } + ] + } +} +explain format=json select * from t2 where year(a) > -5; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "year(t2.a) > -5" + } + } + ] + } +} +explain format=json select * from t2 where year(a) < -1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "year(t2.a) < -1" + } + } + ] + } +} +explain format=json select * from t2 where year(a) <= 10000; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "year(t2.a) <= 10000" + } + } + ] + } +} +explain format=json select * from t2 where year(a) >= 10020; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "year(t2.a) >= 10020" + } + } + ] + } +} +explain format=json select * from t2 where date(a) = '10000-01-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "cast(t2.a as date) = '10000-01-01'" + } + } + ] + } +} +Warnings: +Warning 1292 Incorrect datetime value: '10000-01-01' +explain format=json select * from t2 where date(a) < '-1-01-01'; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": 0.16810023, + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 1006, + "cost": 0.16810023, + "filtered": 100, + "attached_condition": "cast(t2.a as date) < '-1-01-01'" + } + } + ] + } +} +Warnings: +Warning 1292 Incorrect datetime value: '-1-01-01' +drop table t0,t1,t2,t3; +drop view v1; +drop procedure sp; diff --git a/mysql-test/main/sargable_date_cond.test b/mysql-test/main/sargable_date_cond.test new file mode 100644 index 00000000000..21ec9f7ddd0 --- /dev/null +++ b/mysql-test/main/sargable_date_cond.test @@ -0,0 +1,539 @@ +# +# MDEV-8320: Allow index usage for DATE(datetime_column) = const +# + +--disable_warnings +drop table if exists t0,t1,t2,t3; +--enable_warnings + +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1(a int); +insert into t1 select A.a + B.a* 10 from t0 A, t0 B; + +create table t2 (pk int primary key, a datetime, b date, key(a), key(b)); +insert into t2 +select + A.a*10+B.a, + date_add(date_add('2017-01-01', interval A.a*8 day), interval B.a hour), + date_add('2017-01-01', interval A.a*7 day) +from t1 A, t0 B; + +--echo # +--echo # "YEAR(datetime_col) CMP year_value", basic checks +--echo # +let $q= select count(*) from t2 where year(a) < 2018; +eval $q; +select count(*) from t2 where a < '2018-01-01'; +explain format=json select * from t2 force index(a) where year(a) < 2018; +--echo # Check rewrite for a prepared statement: +execute immediate + "explain format=json select * from t2 force index(a) where year(a) < ?" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +--echo # Prepared statement with a placeholder +prepare stmt from "select count(*) from t2 where year(a) < ?"; +execute stmt using 2018; +execute stmt using 2017; + +let $q= select count(*) from t2 where year(a) <= 2018; +eval $q; +select count(*) from t2 where a < '2019-01-01'; +explain format=json select * from t2 force index(a) where year(a) <= 2018; +execute immediate + "explain format=json select * from t2 force index(a) where year(a) <= ?" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where year(a) <= ?"; +execute stmt using 2018; +execute stmt using 2017; + + +let $q= select count(*) from t2 where year(a) > 2018; +eval $q; +select count(*) from t2 where a > '2018-12-31 23:59:59.999999'; +explain format=json select * from t2 force index(a) where year(a) > 2018; +execute immediate + "explain format=json select * from t2 force index(a) where year(a) > ?" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where year(a) > ?"; +execute stmt using 2018; +execute stmt using 2017; + +let $q= select count(*) from t2 where year(a) >= 2018; +eval $q; +select count(*) from t2 where a >= '2018-01-01'; +explain format=json select * from t2 force index(a) where year(a) >= 2018; +execute immediate + "explain format=json select * from t2 force index(a) where year(a) >= ?" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where year(a) >= ?"; +execute stmt using 2018; +execute stmt using 2019; + +let $q= select count(*) from t2 where year(a) = 2017; +eval $q; +select count(*) from t2 where a >= '2017-01-01' and a < '2018-01-01'; +explain format=json select * from t2 force index(a) where year(a) = 2017; +execute immediate + "explain format=json select * from t2 force index(a) where year(a) = ?" + using 2017; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where year(a) = ?"; +execute stmt using 2017; +execute stmt using 2019; + +--echo # +--echo # "YEAR(datetime_col) CMP year_value", reverse argument order +--echo # +let $q= select count(*) from t2 where 2017 < year(a); +eval $q; +select count(*) from t2 where a >= '2018-01-01'; +explain format=json select * from t2 force index(a) where 2017 < year(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? < year(a)" + using 2017; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? < year(a)"; +execute stmt using 2017; +execute stmt using 2018; + +let $q= select count(*) from t2 where 2018 <= year(a); +select count(*) from t2 where a >= '2018-01-01'; +explain format=json select * from t2 force index(a) where 2018 <= year(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? <= year(a)" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? <= year(a)"; +execute stmt using 2018; +execute stmt using 2019; + +let $q= select count(*) from t2 where 2018 > year(a); +eval $q; +select count(*) from t2 where a < '2018-01-01'; +explain format=json select * from t2 force index(a) where 2018 > year(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? > year(a)" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? > year(a)"; +execute stmt using 2018; +execute stmt using 2019; + +let $q= select count(*) from t2 where 2018 >= year(a); +select count(*) from t2 where a < '2019-01-01'; +explain format=json select * from t2 force index(a) where 2018 >= year(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? >= year(a)" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? >= year(a)"; +execute stmt using 2018; +execute stmt using 2019; + +let $q= select count(*) from t2 where 2018 = year(a); +eval $q; +select count(*) from t2 where a >= '2018-01-01' and a < '2019-01-01'; +explain format=json select * from t2 force index(a) where 2018 = year(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? = year(a)" + using 2018; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? = year(a)"; +execute stmt using 2018; +execute stmt using 2019; + +--echo # +--echo # "DATE(datetime_col) CMP date_value", basic checks +--echo # +let $q= select count(*) from t2 where date(a) < '2017-06-01'; +eval $q; +select count(*) from t2 where a < '2017-06-01'; +explain format=json select * from t2 force index(a) where date(a)< '2017-06-01'; +execute immediate + "explain format=json select * from t2 force index(a) where date(a) < ?" + using '2017-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(a) < ?"; +execute stmt using '2017-06-01'; +execute stmt using '2017-06-05'; + +let $q= select count(*) from t2 where date(a) <= '2017-06-03'; +eval $q; +select count(*) from t2 where a < '2017-06-04'; +explain format=json select * from t2 force index(a) where date(a)<='2017-06-04'; +execute immediate + "explain format=json select * from t2 force index(a) where date(a) <= ?" + using '2017-06-04'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(a) <= ?"; +execute stmt using '2017-06-03'; +execute stmt using '2017-06-10'; + +let $q= select count(*) from t2 where date(a) > '2018-06-01'; +eval $q; +select count(*) from t2 where a >= '2018-06-02'; +explain format=json select * from t2 force index(a) where date(a)> '2018-06-01'; +execute immediate + "explain format=json select * from t2 force index(a) where date(a) > ?" + using '2018-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(a) > ?"; +execute stmt using '2018-06-01'; +execute stmt using '2018-06-05'; + +let $q= select count(*) from t2 where date(a) >= '2018-06-01'; +eval $q; +select count(*) from t2 where a >= '2018-06-01'; +explain format=json select * from t2 force index(a) where date(a)>='2018-06-01'; +execute immediate + "explain format=json select * from t2 force index(a) where date(a) >= ?" + using '2018-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(a) >= ?"; +execute stmt using '2018-06-01'; +execute stmt using '2018-06-10'; + +let $q= select count(*) from t2 where date(a) = '2017-06-02'; +eval $q; +select count(*) from t2 where a >= '2017-06-02' and a < '2017-06-03'; +explain format=json select * from t2 force index(a) where date(a)= '2017-06-02'; +execute immediate + "explain format=json select * from t2 force index(a) where date(a) = ?" + using '2017-06-02'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(a) = ?"; +execute stmt using '2017-06-02'; +execute stmt using '2017-06-05'; + +--echo # +--echo # "DATE(datetime_col) CMP date_value", reverse order +--echo # +let $q= select count(*) from t2 where '2017-06-01' > date(a); +eval $q; +select count(*) from t2 where '2017-06-01' > a; +explain format=json select * from t2 force index(a) where '2017-06-01' > date(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? > date(a)" + using '2017-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? > date(a)"; +execute stmt using '2017-06-01'; +execute stmt using '2017-06-05'; + +let $q= select count(*) from t2 where '2017-06-03' >= date(a); +eval $q; +select count(*) from t2 where '2017-06-03' >= a; +explain format=json select * from t2 force index(a) where '2017-06-03' >= date(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? >= date(a)" + using '2017-06-03'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? >= date(a)"; +execute stmt using '2017-06-03'; +execute stmt using '2017-06-12'; + +let $q= select count(*) from t2 where '2018-06-01' < date(a); +eval $q; +select count(*) from t2 where '2018-06-02' <= a; +explain format=json select * from t2 force index(a) where '2018-06-01' < date(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? < date(a)" + using '2017-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? < date(a)"; +execute stmt using '2018-06-02'; +execute stmt using '2018-06-15'; + +let $q= select count(*) from t2 where '2018-06-01' <= date(a); +eval $q; +select count(*) from t2 where '2018-06-01' <= a; +explain format=json select * from t2 force index(a) where '2018-06-01' <= date(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? <= date(a)" + using '2017-06-01'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? <= date(a)"; +execute stmt using '2018-06-01'; +execute stmt using '2018-06-15'; + +let $q= select count(*) from t2 where '2017-06-02' = date(a); +eval $q; +select count(*) from t2 where a >= '2017-06-02' and a < '2017-06-03'; +explain format=json select * from t2 force index(a) where '2017-06-02' = date(a); +execute immediate + "explain format=json select * from t2 force index(a) where ? = date(a)" + using '2017-06-02'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where ? = date(a)"; +execute stmt using '2017-06-03'; +execute stmt using '2017-06-10'; + +--echo # Check rewrite of a more complicated query +explain format=json select * from t2 as t21 force index(a), + t2 as t22 force index(a) + where year(t21.a) < 2018 and t21.b > '2017-11-01' + and date(t22.a) >= '2017-02-01' and t22.b > '2017-11-01'; + +--echo # +--echo # Incorrect const values processing (no rewrite is possible) +--echo # +explain format=json select * from t2 where year(a) = -1; +explain format=json select * from t2 where year(a) > -5; +explain format=json select * from t2 where year(a) < -1; +explain format=json select * from t2 where year(a) <= 10000; +explain format=json select * from t2 where year(a) >= 10020; +explain format=json select * from t2 where date(a) = '10000-01-01'; +explain format=json select * from t2 where date(a) < '-1-01-01'; + +--echo # +--echo # Try DATE function and DATE (not DATETIME) column: +--echo # +let $q= select count(*) from t2 where date(b)< '2017-06-03'; +eval $q; +select count(*) from t2 where b < '2017-06-03'; +explain format=json select * from t2 force index(b) where date(b)< '2017-06-03'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(b) < ?"; +execute stmt using '2017-06-03'; +execute stmt using '2017-06-10'; + +let $q= select count(*) from t2 force index(b) where date(b)= '2017-06-04'; +eval $q; +select count(*) from t2 where b >= '2017-06-04' and b < '2017-06-05'; +explain format=json select * from t2 force index(b) where date(b)='2017-06-04'; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +prepare stmt from "select count(*) from t2 where date(b) = ?"; +execute stmt using '2017-06-04'; +execute stmt using '2017-06-10'; + +--echo # +--echo # Check actual query results +--echo # +insert into t2 values (10001,'2006-12-31 23:59:59','2006-12-31'); +insert into t2 values (10002,'2007-01-01 00:00:00','2007-01-01'); +insert into t2 values (10003,'2007-12-31 23:59:59','2007-12-31'); +insert into t2 values (10004,'2008-01-01 00:00:00','2008-01-01'); + +explain format=json +select * from t2 force index(b) where year(b)=2007; +select * from t2 force index(b) where year(b)=2007; + +insert into t2 values (10010,'2006-12-31 00:00:00','2006-12-31'); +insert into t2 values (10011,'2006-12-30 23:59:59','2006-12-30'); + +explain format=json +select * from t2 force index(a) where date(a)='2006-12-31'; +select * from t2 force index(a) where date(a)='2006-12-31'; + +--echo # +--echo # Test the TIMESTAMP column +--echo # +create table t3 (a timestamp, b date, key(a)); +# Insert data starting from 2016 since that year had a leap second +# (https://en.wikipedia.org/wiki/Leap_second) +set time_zone="UTC"; # To make sure we avoid daylight saving time shifts +insert into t3 + select + timestampadd(hour, B.a, date_add('2016-01-01', interval A.a*8 day)), + date_add('2016-01-01', interval A.a*7 day) + from t1 A, t0 B; + +--echo # Results of those two queries must be equal: +let $q= select count(*) from t3 force index(a) where year(a)= 2016; +eval $q; +--echo # The result must be the same as this query's: +select count(*) from t3 force index(a) where a >= '2016-01-01 00:00:00' + and a <= '2016-12-31 23:59:59.999999'; +explain format=json + select count(*) from t3 force index(a) where year(a)= 2016; +--echo # Check rewrite for a prepared statement: +execute immediate + "explain format=json select * from t3 force index(a) where year(a) < ?" + using 2017; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +eval create or replace view v1 as $q; +select * from v1; +eval create or replace procedure sp() $q; +call sp(); +call sp(); +--echo # Prepared statement with a placeholder +prepare stmt from "select count(*) from t3 where year(a) < ?"; +execute stmt using 2017; +execute stmt using 2018; +set time_zone= @@global.time_zone; + +--echo # +--echo # Incorrect const values processing (no rewrite is possible) +--echo # +explain format=json select * from t2 where year(a) = -1; +explain format=json select * from t2 where year(a) > -5; +explain format=json select * from t2 where year(a) < -1; +explain format=json select * from t2 where year(a) <= 10000; +explain format=json select * from t2 where year(a) >= 10020; +explain format=json select * from t2 where date(a) = '10000-01-01'; +explain format=json select * from t2 where date(a) < '-1-01-01'; + +drop table t0,t1,t2,t3; +drop view v1; +drop procedure sp; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 2ac802314ee..9ef1ee46d5c 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -111,7 +111,9 @@ SET (SQL_SOURCE mf_iocache.cc my_decimal.cc mysqld.cc net_serv.cc keycaches.cc ../sql-common/client_plugin.c - opt_range.cc opt_sum.cc + opt_range.cc + opt_rewrite_date_cmp.cc + opt_sum.cc ../sql-common/pack.c parse_file.cc password.c procedure.cc protocol.cc records.cc repl_failsafe.cc rpl_filter.cc session_tracker.cc diff --git a/sql/item.cc b/sql/item.cc index 92f3d55fcf9..499d23aa0f2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6957,7 +6957,8 @@ Item *Item_int::clone_item(THD *thd) } -void Item_datetime::set(longlong packed, enum_mysql_timestamp_type ts_type) +void Item_datetime::set_from_packed(longlong packed, + enum_mysql_timestamp_type ts_type) { unpack_time(packed, <ime, ts_type); } @@ -6973,6 +6974,16 @@ longlong Item_datetime::val_int() return TIME_to_ulonglong(<ime); } +void Item_datetime::print(String *str, enum_query_type query_type) +{ + Datetime dt(current_thd, this); + String dt_str; + dt.to_string(&dt_str, decimals); + str->append('\''); + str->append(dt_str); + str->append('\''); +} + int Item_decimal::save_in_field(Field *field, bool no_conversions) { field->set_notnull(); diff --git a/sql/item.h b/sql/item.h index 5956b810d51..5b8b858c13c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2514,6 +2514,8 @@ public: { return this; } virtual Item *multiple_equality_transformer(THD *thd, uchar *arg) { return this; } + virtual Item* date_conds_transformer(THD *thd, uchar *arg) + { return this; } virtual bool expr_cache_is_needed(THD *) { return FALSE; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const @@ -4514,12 +4516,14 @@ public: int save_in_field(Field *field, bool no_conversions) override; longlong val_int() override; double val_real() override { return (double)val_int(); } - void set(longlong packed, enum_mysql_timestamp_type ts_type); + void set(const MYSQL_TIME *datetime) { ltime= *datetime; } + void set_from_packed(longlong packed, enum_mysql_timestamp_type ts_type); bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) override { *to= ltime; return false; } + void print(String *str, enum_query_type query_type) override; }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index fcdb2aaf2d4..b5f747219a5 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -7833,3 +7833,4 @@ Item *Item_equal::multiple_equality_transformer(THD *thd, uchar *arg) break; } } + diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index efe0bf59559..7f808580470 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -25,6 +25,7 @@ #include "item_func.h" /* Item_int_func, Item_bool_func */ #include "item.h" +#include "opt_rewrite_date_cmp.h" extern Item_result item_cmp_type(Item_result a,Item_result b); inline Item_result item_cmp_type(const Item *a, const Item *b) @@ -789,6 +790,8 @@ public: friend class Arg_comparator; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_eq>(thd, this); } + Item* date_conds_transformer(THD *thd, uchar *arg) override + { return do_date_conds_transformation(thd, this); } }; class Item_func_equal final :public Item_bool_rowready_func2 @@ -838,6 +841,8 @@ public: Item *negated_item(THD *thd) override; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_ge>(thd, this); } + Item* date_conds_transformer(THD *thd, uchar *arg) override + { return do_date_conds_transformation(thd, this); } }; @@ -858,6 +863,8 @@ public: Item *negated_item(THD *thd) override; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_gt>(thd, this); } + Item* date_conds_transformer(THD *thd, uchar *arg) override + { return do_date_conds_transformation(thd, this); } }; @@ -878,6 +885,8 @@ public: Item *negated_item(THD *thd) override; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_le>(thd, this); } + Item* date_conds_transformer(THD *thd, uchar *arg) override + { return do_date_conds_transformation(thd, this); } }; @@ -898,6 +907,8 @@ public: Item *negated_item(THD *thd) override; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_lt>(thd, this); } + Item* date_conds_transformer(THD *thd, uchar *arg) override + { return do_date_conds_transformation(thd, this); } }; @@ -1584,7 +1595,7 @@ public: { packed_longlong *val= reinterpret_cast<packed_longlong*>(base)+pos; Item_datetime *dt= static_cast<Item_datetime*>(item); - dt->set(val->val, type_handler()->mysql_timestamp_type()); + dt->set_from_packed(val->val, type_handler()->mysql_timestamp_type()); } friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b); }; diff --git a/sql/item_func.cc b/sql/item_func.cc index a94dcefe32f..61b3304a2b6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -277,7 +277,6 @@ bool Item_func::check_argument_types_scalar(uint start, uint end) const return false; } - /* Resolve references to table column for a function and its argument @@ -362,6 +361,7 @@ Item_func::fix_fields(THD *thd, Item **ref) if (fix_length_and_dec(thd)) return TRUE; base_flags|= item_base_t::FIXED; + return FALSE; } diff --git a/sql/item_func.h b/sql/item_func.h index 6e714814526..dcb04a62727 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -78,6 +78,7 @@ public: JSON_EXTRACT_FUNC, JSON_VALID_FUNC, ROWNUM_FUNC, CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider CASE_SIMPLE_FUNC, // Used by ColumnStore/spider, + DATE_FUNC, YEAR_FUNC }; static scalar_comparison_op functype_to_scalar_comparison_op(Functype type) { diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a5f6d9307c6..953984e4c72 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -462,6 +462,7 @@ public: static LEX_CSTRING name= {STRING_WITH_LEN("year") }; return name; } + enum Functype functype() const override { return YEAR_FUNC; } enum_monotonicity_info get_monotonicity_info() const override; longlong val_int_endpoint(bool left_endp, bool *incl_endp) override; bool fix_length_and_dec(THD *thd) override @@ -1334,6 +1335,7 @@ public: { print_cast_temporal(str, query_type); } + enum Functype functype() const override { return DATE_FUNC; } bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override; bool fix_length_and_dec(THD *thd) override { @@ -2025,6 +2027,4 @@ public: return false; } }; - - #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/opt_rewrite_date_cmp.cc b/sql/opt_rewrite_date_cmp.cc new file mode 100644 index 00000000000..234dbb09311 --- /dev/null +++ b/sql/opt_rewrite_date_cmp.cc @@ -0,0 +1,363 @@ +/* + Copyright (c) 2023, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + + +/** + @file + + @brief + Rewrites that make non-sargable date[time] comparisons sargable. +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "mariadb.h" +#include "sql_priv.h" +#include "my_json_writer.h" +#include "opt_rewrite_date_cmp.h" + +Date_cmp_func_rewriter::Date_cmp_func_rewriter(THD* thd, + Item_func_eq *item_func) : + thd(thd), + result(item_func) +{ + if (!check_cond_match_and_prepare(item_func)) + return; + + /* + This is an equality. Do a rewrite like this: + "YEAR(col) = val" -> col BETWEEN year_start(val) AND year_end(val) + "DATE(col) = val" -> col BETWEEN day_start(val) AND day_end(val) + */ + Item *start_bound, *end_bound; + if (!(start_bound= create_start_bound()) || !(end_bound= create_end_bound())) + return; + Item *new_cond; + if (!(new_cond= new (thd->mem_root) Item_func_between(thd, field_ref, + start_bound, end_bound))) + return; + if (!new_cond->fix_fields(thd, &new_cond)) + result= new_cond; +} + + +Date_cmp_func_rewriter::Date_cmp_func_rewriter(THD* thd, + Item_func_ge *item_func) : + thd(thd), + result(item_func) +{ + if (!check_cond_match_and_prepare(item_func)) + return; + rewrite_le_gt_lt_ge(); +} + + +Date_cmp_func_rewriter::Date_cmp_func_rewriter(THD* thd, + Item_func_lt *item_func) : + thd(thd), + result(item_func) +{ + if (!check_cond_match_and_prepare(item_func)) + return; + rewrite_le_gt_lt_ge(); +} + + +Date_cmp_func_rewriter::Date_cmp_func_rewriter(THD* thd, + Item_func_gt *item_func) : + thd(thd), + result(item_func) +{ + if (!check_cond_match_and_prepare(item_func)) + return; + rewrite_le_gt_lt_ge(); +} + + +Date_cmp_func_rewriter::Date_cmp_func_rewriter(THD* thd, + Item_func_le*item_func) : + thd(thd), + result(item_func) +{ + if (!check_cond_match_and_prepare(item_func)) + return; + rewrite_le_gt_lt_ge(); +} + + +bool Date_cmp_func_rewriter::check_cond_match_and_prepare( + Item_bool_rowready_func2 *item_func) +{ + if (thd->lex->is_ps_or_view_context_analysis()) + { + DBUG_ASSERT(0); + return false; + } + + Item **args= item_func->arguments(); + rewrite_func_type= item_func->functype(); + bool condition_matches= false; + const Type_handler *comparison_type= item_func->get_comparator()-> + compare_type_handler(); + + /* + Check if this is "YEAR(indexed_col) CMP const_item" or + "DATE(indexed_col) CMP const_item" + */ + if ((field_ref= is_date_rounded_field(args[0], comparison_type, + &argument_func_type)) && + args[1]->basic_const_item()) + { + const_arg_value= args[1]; + condition_matches= true; + } + else + /* + Check if this is "const_item CMP YEAR(indexed_col)" or + "const_item CMP DATE(indexed_col)" + */ + if ((field_ref= is_date_rounded_field(args[1], comparison_type, + &argument_func_type)) && + args[0]->basic_const_item()) + { + /* + Ok, the condition has form like "const<YEAR(col)"/"const<DATE(col)". + Turn it around to be "YEAR(col)>const"/"DATE(col)>const" + */ + const_arg_value= args[0]; + + rewrite_func_type= item_func->rev_functype(); + condition_matches= true; + } + return condition_matches; +} + +/* + Check if the passed item is YEAR(key_col) or DATE(key_col). + + Also + - key_col must be covered by an index usable by the current query + - key_col must have a DATE[TIME] or TIMESTAMP type + - The value of the YEAR(..) or DATE(..) function must be compared using an a + appropriate comparison_type. + + @param item IN Item to check + @param comparison_type IN Which datatype is used to compare the item value + @param out_func_type OUT Function (is it YEAR or DATE) + + @return + key_col if the check suceeded + NULL otherwise +*/ +Item_field *Date_cmp_func_rewriter::is_date_rounded_field(Item* item, + const Type_handler *comparison_type, + Item_func::Functype *out_func_type) const +{ + if (item->type() != Item::FUNC_ITEM) + return nullptr; + + Item_func::Functype func_type= ((Item_func*)item)->functype(); + bool function_ok= false; + switch (func_type) { + case Item_func::YEAR_FUNC: + // The value of YEAR(x) must be compared as integer + if (comparison_type == &type_handler_slonglong) + function_ok= true; + break; + case Item_func::DATE_FUNC: + // The value of DATE(x) must be compared as dates. + if (comparison_type == &type_handler_newdate) + function_ok= true; + break; + default: + ;// do nothing + } + + if (function_ok) + { + Item* arg= ((Item_func*)item)->arguments()[0]; + // Check if the argument is a column that's covered by some index + if (arg->real_item()->type() == Item::FIELD_ITEM) + { + Item_field *item_field= (Item_field*)(arg->real_item()); + const key_map * used_indexes= + &item_field->field->table->keys_in_use_for_query; + enum_field_types field_type= item_field->field_type(); + if ((field_type == MYSQL_TYPE_DATE || + field_type == MYSQL_TYPE_DATETIME || + field_type == MYSQL_TYPE_NEWDATE || + field_type == MYSQL_TYPE_TIMESTAMP) && + item_field->field->part_of_key.is_overlapping(*used_indexes)) + { + *out_func_type= func_type; + return item_field; + } + } + } + return nullptr; +} + + +void Date_cmp_func_rewriter::rewrite_le_gt_lt_ge() +{ + if (rewrite_func_type == Item_func::LE_FUNC || + rewrite_func_type == Item_func::GT_FUNC) + { + const_arg_value= create_end_bound(); + } + else if (rewrite_func_type == Item_func::LT_FUNC || + rewrite_func_type == Item_func::GE_FUNC) + { + const_arg_value= create_start_bound(); + } + if (!const_arg_value) + return; + Item *repl= create_cmp_func(rewrite_func_type, field_ref, const_arg_value); + if (!repl) + return; + if (!repl->fix_fields(thd, &repl)) + result= repl; +} + + +Item *Date_cmp_func_rewriter::create_start_bound() +{ + Item_datetime *res; + MYSQL_TIME const_arg_ts; + memset(&const_arg_ts, 0, sizeof(const_arg_ts)); + const_arg_ts.time_type= MYSQL_TIMESTAMP_DATETIME; + switch (argument_func_type) { + case Item_func::YEAR_FUNC: + const_arg_ts.year= static_cast<unsigned int>(const_arg_value->val_int()); + const_arg_ts.month= 1; + const_arg_ts.day= 1; + if (check_datetime_range(&const_arg_ts)) + return nullptr; + res= new (thd->mem_root) Item_datetime(thd); + res->set(&const_arg_ts); + break; + case Item_func::DATE_FUNC: + if (field_ref->field->type() == MYSQL_TYPE_DATE) + return const_arg_value; + else + { + Datetime const_arg_dt(current_thd, const_arg_value); + if (!const_arg_dt.is_valid_datetime()) + return nullptr; + res= new (thd->mem_root) Item_datetime(thd); + const_arg_dt.copy_to_mysql_time(&const_arg_ts); + const_arg_ts.second_part= const_arg_ts.second= + const_arg_ts.minute= const_arg_ts.hour= 0; + const_arg_ts.time_type= MYSQL_TIMESTAMP_DATETIME; + res->set(&const_arg_ts); + } + break; + default: + DBUG_ASSERT(0); + res= nullptr; + break; + } + return res; +} + + +Item *Date_cmp_func_rewriter::create_end_bound() +{ + Item_datetime *res; + MYSQL_TIME const_arg_ts; + memset(&const_arg_ts, 0, sizeof(const_arg_ts)); + const_arg_ts.time_type= MYSQL_TIMESTAMP_DATETIME; + switch (argument_func_type) { + case Item_func::YEAR_FUNC: + const_arg_ts.year= static_cast<unsigned int>(const_arg_value->val_int()); + const_arg_ts.month= 12; + const_arg_ts.day= 31; + const_arg_ts.hour= 23; + const_arg_ts.minute= TIME_MAX_MINUTE; + const_arg_ts.second= TIME_MAX_SECOND; + const_arg_ts.second_part= TIME_MAX_SECOND_PART; + if (check_datetime_range(&const_arg_ts)) + return nullptr; + res= new (thd->mem_root) Item_datetime(thd); + res->set(&const_arg_ts); + break; + case Item_func::DATE_FUNC: + if (field_ref->field->type() == MYSQL_TYPE_DATE) + return const_arg_value; + else + { + res= new (thd->mem_root) Item_datetime(thd); + Datetime const_arg_dt(current_thd, const_arg_value); + if (!const_arg_dt.is_valid_datetime()) + return nullptr; + const_arg_dt.copy_to_mysql_time(&const_arg_ts); + const_arg_ts.hour= 23; + const_arg_ts.minute= TIME_MAX_MINUTE; + const_arg_ts.second= TIME_MAX_SECOND; + const_arg_ts.second_part=TIME_MAX_SECOND_PART; + const_arg_ts.time_type= MYSQL_TIMESTAMP_DATETIME; + res->set(&const_arg_ts); + } + break; + default: + DBUG_ASSERT(0); + res= nullptr; + break; + } + return res; +} + + +/* + Create an Item for "arg1 $CMP arg2", where $CMP is specified by func_type. +*/ +Item *Date_cmp_func_rewriter::create_cmp_func(Item_func::Functype func_type, + Item *arg1, Item *arg2) +{ + Item *res; + switch (func_type) { + case Item_func::GE_FUNC: + res= new (thd->mem_root) Item_func_ge(thd, arg1, arg2); + break; + case Item_func::GT_FUNC: + res= new (thd->mem_root) Item_func_gt(thd, arg1, arg2); + break; + case Item_func::LE_FUNC: + res= new (thd->mem_root) Item_func_le(thd, arg1, arg2); + break; + case Item_func::LT_FUNC: + res= new (thd->mem_root) Item_func_lt(thd, arg1, arg2); + break; + default: + DBUG_ASSERT(0); + res= NULL; + } + return res; +} + +void trace_date_item_rewrite(THD *thd, Item *new_item, Item *old_item) +{ + if (new_item != old_item) + { + Json_writer_object trace_wrapper(thd); + trace_wrapper.add("transformation", "date_conds_into_sargable") + .add("before", old_item) + .add("after", new_item); + } +} + diff --git a/sql/opt_rewrite_date_cmp.h b/sql/opt_rewrite_date_cmp.h new file mode 100644 index 00000000000..9f6e1d74ffa --- /dev/null +++ b/sql/opt_rewrite_date_cmp.h @@ -0,0 +1,111 @@ +/* + Copyright (c) 2023, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef OPT_REWRITE_DATE_CMP_INCLUDED +#define OPT_REWRITE_DATE_CMP_INCLUDED + +class Item_func_eq; +class Item_func_ge; +class Item_func_gt; +class Item_func_le; +class Item_func_lt; +class Item_bool_rowready_func2; + +/* + @brief Class responsible for rewriting datetime comparison condition. + It rewrites non-sargable conditions into sargable. + + @detail + The intent of this class is to do equivalent rewrites as follows: + + YEAR(col) <= val -> col <= year_end(val) + YEAR(col) < val -> col < year_start(val) + YEAR(col) >= val -> col >= year_start(val) + YEAR(col) > val -> col > year_end(val) + YEAR(col) = val -> col >= year_start(val) AND col<=year_end(val) + + Also the same is done for comparisons with DATE(col): + + DATE(col) <= val -> col <= day_end(val) + + if col has a DATE type (not DATETIME), then the rewrite becomes: + + DATE(col) <= val -> col <= val + + @usage + Date_cmp_func_rewriter rwr(thd, item_func); + Item *new_item= rwr.get_rewrite_result(); + + Returned new_item points to an item that item_func was rewritten to. + new_item already has fixed fields (fix_fields() was called). + If no rewrite happened, new_item points to the initial item_func parameter + + @todo + Also handle conditions in form "YEAR(date_col) BETWEEN 2014 AND 2017" + and "YEAR(col) = c1 AND MONTH(col) = c2" +*/ +class Date_cmp_func_rewriter +{ +public: + Date_cmp_func_rewriter(THD* thd, Item_func_eq *item_func); + + Date_cmp_func_rewriter(THD* thd, Item_func_ge *item_func); + + Date_cmp_func_rewriter(THD* thd, Item_func_gt *item_func); + + Date_cmp_func_rewriter(THD* thd, Item_func_le *item_func); + + Date_cmp_func_rewriter(THD* thd, Item_func_lt *item_func); + + Item* get_rewrite_result() const { return result; } + + Date_cmp_func_rewriter() = delete; + Date_cmp_func_rewriter(const Date_cmp_func_rewriter&) = delete; + Date_cmp_func_rewriter(Date_cmp_func_rewriter&&) = delete; + +private: + bool check_cond_match_and_prepare(Item_bool_rowready_func2 *item_func); + Item_field *is_date_rounded_field(Item* item, + const Type_handler *comparison_type, + Item_func::Functype *out_func_type) const; + void rewrite_le_gt_lt_ge(); + Item *create_start_bound(); + Item *create_end_bound(); + Item *create_cmp_func(Item_func::Functype func_type, Item *arg1, Item *arg2); + + THD *thd= nullptr; + Item *const_arg_value= nullptr; + Item_func::Functype rewrite_func_type= Item_func::UNKNOWN_FUNC; + Item_func::Functype argument_func_type= Item_func::UNKNOWN_FUNC; + Item_field *field_ref= nullptr; + Item *result= nullptr; +}; + + +void trace_date_item_rewrite(THD *thd,Item *new_item, Item *old_item); + +template<typename T> +Item* do_date_conds_transformation(THD *thd, T *item) +{ + Date_cmp_func_rewriter rwr(thd, item); + /* If the rewrite failed for some reason, we get the original item */ + Item *new_item= rwr.get_rewrite_result(); + trace_date_item_rewrite(thd, new_item, item); + return new_item; +} + + +#endif diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5a7fa14916f..acf23d5ffa2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2348,6 +2348,20 @@ public: bool uses_stored_routines() const { return sroutines_list.elements != 0; } + void set_date_funcs_used_flag() + { + date_funcs_used_flag= true; + } + + /* + Returns TRUE if date functions such as YEAR(), MONTH() or DATE() + are used in this LEX + */ + bool are_date_funcs_used() const + { + return date_funcs_used_flag; + } + private: /** @@ -2388,6 +2402,12 @@ private: be accessed while executing a statement. */ uint32 stmt_accessed_table_flag; + + /* + Flag indicating that date functions such as YEAR(), MONTH() or DATE() are + used in this LEX + */ + bool date_funcs_used_flag= false; }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4b01a96a132..e98121231a7 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2297,6 +2297,8 @@ JOIN::optimize_inner() } transform_in_predicates_into_equalities(thd); + if (thd->lex->are_date_funcs_used()) + transform_date_conds_into_sargable(); conds= optimize_cond(this, conds, join_list, ignore_on_expr, &cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS); @@ -32286,6 +32288,20 @@ bool JOIN::transform_in_predicates_into_equalities(THD *thd) /** @brief + Rewrite datetime comparison conditions into sargable. + See details in the description for class Date_cmp_func_rewriter +*/ + +bool JOIN::transform_date_conds_into_sargable() +{ + DBUG_ENTER("JOIN::transform_date_conds_into_sargable"); + DBUG_RETURN(transform_all_conds_and_on_exprs( + thd, &Item::date_conds_transformer)); +} + + +/** + @brief Transform all items in WHERE and ON expressions using a given transformer @param thd The context of the statement diff --git a/sql/sql_select.h b/sql/sql_select.h index f908484444b..0d53d4c9798 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1920,6 +1920,7 @@ private: void free_pushdown_handlers(List<TABLE_LIST>& join_list); void init_join_cache_and_keyread(); bool transform_in_predicates_into_equalities(THD *thd); + bool transform_date_conds_into_sargable(); bool transform_all_conds_and_on_exprs(THD *thd, Item_transformer transformer); bool transform_all_conds_and_on_exprs_in_join_list(THD *thd, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ccce38a4c60..c8a9ec0cb41 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9867,6 +9867,7 @@ function_call_keyword: $$= new (thd->mem_root) Item_date_typecast(thd, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; + Lex->set_date_funcs_used_flag(); } | DAY_SYM '(' expr ')' { @@ -9929,6 +9930,7 @@ function_call_keyword: $$= new (thd->mem_root) Item_func_month(thd, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; + Lex->set_date_funcs_used_flag(); } | RIGHT '(' expr ',' expr ')' { @@ -9979,6 +9981,7 @@ function_call_keyword: $$= new (thd->mem_root) Item_func_year(thd, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; + Lex->set_date_funcs_used_flag(); } ; |