mirror of
https://github.com/MariaDB/server.git
synced 2025-04-18 21:44:20 +03:00
MDEV-33299 Assertion `(tm->tv_usec % (int) log_10_int[6 - dec]) == 0' failed in void my_timestamp_to_binary(const timeval*, uchar*, uint)
This original query: (1) SELECT ts0 FROM t1 WHERE DATE(ts0) <= '2024-01-23'; was rewritten (by MDEV-8320) to: (2) SELECT ts0 FROM t1 WHERE ts0 <= '2024-01-23 23:59.59.999999'; -- DATETIME comparison, Item_datetime on the right side which was further optimized (by MDEV-32148) to: (3) SELECT ts0 FROM t1 WHERE ts0 <= TIMESTAMP/* WITH LOCAL TIME ZONE*/ '2024-01-23 23:59.59.999999'; -- TIMESTAMP comparison, Item_timestamp_literal on the right side The origin of the problem was in (2) - in the MDEV-8320 related code. The recent new code for MDEV-32148 revealed this problem. Item_datetime on step (2) was always created in an inconsistent way: - with Item::decimals==0 - with ltime.second_part==999999, without taking into account the precision of the left side (e.g. ts0 in the above example) On step (3), Item_timestamp_literal was created in an inconsistent way too, because it copied the inconsistent data from step (2): - with Item::decimals==0 (copied from Item_datetime::decimals) - with m_value.tv_usec==999999 (copied from ltime.second_part of Item_datetime) Later, the Item_timestamp_literal performed save_in_field() and crashed in my_timestamp_to_binary() on a DBUG_ASSERT checking consistency between the fractional precision and the fractional seconds value. Fix: On step (2) create Item_datetime with truncating maximum possible second_part value of 999999 according to the the left side fractional second precision. So for example it sets second_part as follows: - 000000 for TIMESTAMP(0) - 999000 for TIMESTAMP(3) - 999999 for TIMESTAMP(6) This automatically makes the code create a consistent Item_timestamp_literal on step (3). This also makes TIMESTAMP comparison work faster, because now Item_timestamp_literal is created with Item::decimals value equal to the Item_field (which is on the other side of the comparison), so the low level function Type_handler_timestamp_common::cmp_native() goes the fastest execution path optimized for the case when both sides have equal fractional precision. Adding a helper class TimeOfDay to reuse the code when populating: - the last datetime point for YEAR() - the last datetime point for DATE() with a given fractional precision. This class also helped to unify the equal code in create_start_bound() and create_end_bound() into a single method create_bound().
This commit is contained in:
parent
83a79ba33b
commit
e71aecfd30
@ -2026,3 +2026,186 @@ DROP TABLE t2;
|
||||
DROP TABLE t1;
|
||||
SET note_verbosity=DEFAULT;
|
||||
# End of 10.6 tests
|
||||
#
|
||||
# Start of 11.3 tests
|
||||
#
|
||||
#
|
||||
# MDEV-33299 Assertion `(tm->tv_usec % (int) log_10_int[6 - dec]) == 0' failed in void my_timestamp_to_binary(const timeval*, uchar*, uint)
|
||||
#
|
||||
CREATE TABLE t1(a TIMESTAMP,KEY(a));
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23' ;
|
||||
a
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
a
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a TIMESTAMP,KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
a
|
||||
2023-12-31 23:59:59
|
||||
2024-01-22 10:20:30
|
||||
2024-01-23 10:20:30
|
||||
2024-01-23 23:59:59
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 5 NULL 4 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-23 23:59:59'
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
a
|
||||
2023-12-31 23:59:59
|
||||
2024-01-22 10:20:30
|
||||
2024-01-23 10:20:30
|
||||
2024-01-23 23:59:59
|
||||
2024-01-24 00:00:00
|
||||
2024-12-31 23:59:59
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 5 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-12-31 23:59:59'
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
a
|
||||
2024-01-24 00:00:00
|
||||
2024-12-31 23:59:59
|
||||
2025-01-01 00:00:00
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 5 NULL 3 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-24 00:00:00'
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
a
|
||||
2024-01-22 10:20:30
|
||||
2024-01-23 10:20:30
|
||||
2024-01-23 23:59:59
|
||||
2024-01-24 00:00:00
|
||||
2024-12-31 23:59:59
|
||||
2025-01-01 00:00:00
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 5 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-01 00:00:00'
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a TIMESTAMP(3),KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30.001');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30.002');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00.000');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00.000');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
a
|
||||
2023-12-31 23:59:59.999
|
||||
2024-01-22 10:20:30.001
|
||||
2024-01-23 10:20:30.002
|
||||
2024-01-23 23:59:59.999
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 7 NULL 4 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-23 23:59:59.999'
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
a
|
||||
2023-12-31 23:59:59.999
|
||||
2024-01-22 10:20:30.001
|
||||
2024-01-23 10:20:30.002
|
||||
2024-01-23 23:59:59.999
|
||||
2024-01-24 00:00:00.000
|
||||
2024-12-31 23:59:59.999
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 7 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-12-31 23:59:59.999'
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
a
|
||||
2024-01-24 00:00:00.000
|
||||
2024-12-31 23:59:59.999
|
||||
2025-01-01 00:00:00.000
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 7 NULL 3 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-24 00:00:00.000'
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
a
|
||||
2024-01-22 10:20:30.001
|
||||
2024-01-23 10:20:30.002
|
||||
2024-01-23 23:59:59.999
|
||||
2024-01-24 00:00:00.000
|
||||
2024-12-31 23:59:59.999
|
||||
2025-01-01 00:00:00.000
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 7 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-01 00:00:00.000'
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a TIMESTAMP(6),KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30.000001');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30.000002');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00.000000');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00.000000');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
a
|
||||
2023-12-31 23:59:59.999999
|
||||
2024-01-22 10:20:30.000001
|
||||
2024-01-23 10:20:30.000002
|
||||
2024-01-23 23:59:59.999999
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 8 NULL 4 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-23 23:59:59.999999'
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
a
|
||||
2023-12-31 23:59:59.999999
|
||||
2024-01-22 10:20:30.000001
|
||||
2024-01-23 10:20:30.000002
|
||||
2024-01-23 23:59:59.999999
|
||||
2024-01-24 00:00:00.000000
|
||||
2024-12-31 23:59:59.999999
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 8 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-12-31 23:59:59.999999'
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
a
|
||||
2024-01-24 00:00:00.000000
|
||||
2024-12-31 23:59:59.999999
|
||||
2025-01-01 00:00:00.000000
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 8 NULL 3 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-24 00:00:00.000000'
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
a
|
||||
2024-01-22 10:20:30.000001
|
||||
2024-01-23 10:20:30.000002
|
||||
2024-01-23 23:59:59.999999
|
||||
2024-01-24 00:00:00.000000
|
||||
2024-12-31 23:59:59.999999
|
||||
2025-01-01 00:00:00.000000
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range a a 8 NULL 6 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2024-01-01 00:00:00.000000'
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# End of 11.3 tests
|
||||
#
|
||||
|
@ -1183,3 +1183,78 @@ DROP TABLE t1;
|
||||
SET note_verbosity=DEFAULT;
|
||||
|
||||
--echo # End of 10.6 tests
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Start of 11.3 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-33299 Assertion `(tm->tv_usec % (int) log_10_int[6 - dec]) == 0' failed in void my_timestamp_to_binary(const timeval*, uchar*, uint)
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1(a TIMESTAMP,KEY(a));
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23' ;
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t1(a TIMESTAMP,KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
CREATE TABLE t1(a TIMESTAMP(3),KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30.001');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30.002');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00.000');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59.999');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00.000');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
CREATE TABLE t1(a TIMESTAMP(6),KEY(a));
|
||||
INSERT INTO t1 VALUES ('2023-12-31 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2024-01-22 10:20:30.000001');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 10:20:30.000002');
|
||||
INSERT INTO t1 VALUES ('2024-01-23 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2024-01-24 00:00:00.000000');
|
||||
INSERT INTO t1 VALUES ('2024-12-31 23:59:59.999999');
|
||||
INSERT INTO t1 VALUES ('2025-01-01 00:00:00.000000');
|
||||
SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) <= '2024-01-23';
|
||||
SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) <= 2024;
|
||||
SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE DATE(a) >= '2024-01-24';
|
||||
SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE YEAR(a) >= 2024;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # End of 11.3 tests
|
||||
--echo #
|
||||
|
@ -4479,6 +4479,13 @@ protected:
|
||||
MYSQL_TIME ltime;
|
||||
public:
|
||||
Item_datetime(THD *thd): Item_int(thd, 0) { unsigned_flag=0; }
|
||||
Item_datetime(THD *thd, const Datetime &dt, decimal_digits_t dec)
|
||||
:Item_int(thd, 0),
|
||||
ltime(*dt.get_mysql_time())
|
||||
{
|
||||
unsigned_flag= 0;
|
||||
decimals= dec;
|
||||
}
|
||||
int save_in_field(Field *field, bool no_conversions) override;
|
||||
longlong val_int() override;
|
||||
double val_real() override { return (double)val_int(); }
|
||||
@ -5014,6 +5021,8 @@ public:
|
||||
:Item_literal(thd),
|
||||
m_value(value)
|
||||
{
|
||||
DBUG_ASSERT(value.is_zero_datetime() ||
|
||||
!value.to_timestamp().fraction_remainder(dec));
|
||||
collation= DTCollation_numeric();
|
||||
decimals= dec;
|
||||
}
|
||||
|
@ -233,91 +233,67 @@ void Date_cmp_func_rewriter::rewrite_le_gt_lt_ge()
|
||||
}
|
||||
|
||||
|
||||
Item *Date_cmp_func_rewriter::create_start_bound()
|
||||
Item *Date_cmp_func_rewriter::create_bound(uint month, uint day,
|
||||
const TimeOfDay6 &td) const
|
||||
{
|
||||
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;
|
||||
/*
|
||||
We could always create Item_datetime with Item_datetime::decimals==6 here.
|
||||
But this would not be efficient in some cases.
|
||||
|
||||
Let's create an Item_datetime with Item_datetime::decimals
|
||||
equal to field_ref->decimals, so if:
|
||||
|
||||
(1) the original statement:
|
||||
|
||||
SELECT ts3 FROM t1 WHERE DATE(ts3) <= '2024-01-23';
|
||||
|
||||
gets rewritten to:
|
||||
|
||||
(2) a statement with DATETIME comparison
|
||||
with an Item_datetime on the right side:
|
||||
|
||||
SELECT ts3 FROM t1
|
||||
WHERE ts3 <= '2024-01-23 23:59.59.999'; -- Item_datetime
|
||||
|
||||
and then gets further rewritten with help of convert_item_for_comparison()
|
||||
to:
|
||||
|
||||
(3) a statement with TIMESTAMP comparison
|
||||
with an Item_timestamp_literal on the right side
|
||||
|
||||
SELECT ts3 FROM t1
|
||||
WHERE ts3 <= '2024-01-23 23:59:59.999'; -- Item_timestamp_literal
|
||||
|
||||
then we have an efficent statement calling
|
||||
Type_handler_timestamp_common::cmp_native() for comparison,
|
||||
which has a faster execution path when both sides have equal
|
||||
fractional precision.
|
||||
*/
|
||||
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;
|
||||
{
|
||||
longlong year= const_arg_value->val_int();
|
||||
const Datetime bound(static_cast<unsigned int>(year), month, day, td);
|
||||
if (!bound.is_valid_datetime())
|
||||
return nullptr; // "year" was out of the supported range
|
||||
return new (thd->mem_root) Item_datetime(thd, bound, field_ref->decimals);
|
||||
}
|
||||
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);
|
||||
const 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);
|
||||
return nullptr; // SQL NULL datetime
|
||||
const Datetime bound(const_arg_dt.time_of_day(td));
|
||||
return new (thd->mem_root) Item_datetime(thd, bound, field_ref->decimals);
|
||||
}
|
||||
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;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -82,8 +82,15 @@ private:
|
||||
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_bound(uint month, uint day, const TimeOfDay6 &td) const;
|
||||
Item *create_start_bound() const
|
||||
{
|
||||
return create_bound(1, 1, TimeOfDay6());
|
||||
}
|
||||
Item *create_end_bound() const
|
||||
{
|
||||
return create_bound(12, 31, TimeOfDay6::end_of_day(field_ref->decimals));
|
||||
}
|
||||
Item *create_cmp_func(Item_func::Functype func_type, Item *arg1, Item *arg2);
|
||||
|
||||
THD *thd= nullptr;
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "mysqld.h"
|
||||
#include "lex_string.h"
|
||||
#include "sql_type_timeofday.h"
|
||||
#include "sql_array.h"
|
||||
#include "sql_const.h"
|
||||
#include "sql_time.h"
|
||||
@ -2520,6 +2521,20 @@ public:
|
||||
}
|
||||
Datetime(my_time_t unix_time, ulong second_part,
|
||||
const Time_zone* time_zone);
|
||||
Datetime(uint year_arg, uint month_arg, uint day_arg, const TimeOfDay6 &td)
|
||||
{
|
||||
neg= 0;
|
||||
year= year_arg;
|
||||
month= month_arg;
|
||||
day= day_arg;
|
||||
hour= td.hour();
|
||||
minute= td.minute();
|
||||
second= td.second();
|
||||
second_part= td.usecond();
|
||||
time_type= MYSQL_TIMESTAMP_DATETIME;
|
||||
if (!is_valid_datetime_slow())
|
||||
time_type= MYSQL_TIMESTAMP_NONE;
|
||||
}
|
||||
|
||||
bool is_valid_datetime() const
|
||||
{
|
||||
@ -2665,6 +2680,12 @@ public:
|
||||
return Temporal::fraction_remainder(dec);
|
||||
}
|
||||
|
||||
Datetime time_of_day(const TimeOfDay6 &td) const
|
||||
{
|
||||
DBUG_ASSERT(is_valid_datetime()); // not SQL NULL
|
||||
return Datetime(year, month, day, td);
|
||||
}
|
||||
|
||||
Datetime &trunc(uint dec)
|
||||
{
|
||||
if (is_valid_datetime())
|
||||
|
67
sql/sql_type_timeofday.h
Normal file
67
sql/sql_type_timeofday.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* Copyright (c) 2024, 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 St, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#ifndef SQL_TYPE_TIMEOFDAY_INCLUDED
|
||||
#define SQL_TYPE_TIMEOFDAY_INCLUDED
|
||||
|
||||
#include "my_time.h" // TIME_MAX_MINUTE
|
||||
|
||||
/*
|
||||
This class stores a time of the day with
|
||||
fractional precision up to 6 digits.
|
||||
*/
|
||||
class TimeOfDay6
|
||||
{
|
||||
uint m_hour; // 0..23
|
||||
uint m_minute; // 0..59
|
||||
uint m_second; // 0..59
|
||||
uint m_usecond; // 0..999999
|
||||
bool is_valid_time_of_day6() const
|
||||
{
|
||||
return m_hour <= 23 &&
|
||||
m_minute <= TIME_MAX_MINUTE &&
|
||||
m_second <= TIME_MAX_SECOND &&
|
||||
m_usecond <= TIME_MAX_SECOND_PART;
|
||||
}
|
||||
public:
|
||||
TimeOfDay6()
|
||||
:m_hour(0), m_minute(0), m_second(0), m_usecond(0)
|
||||
{ }
|
||||
// This constructor assumes the caller passes valid 'hh:mm:ss.ff' values
|
||||
TimeOfDay6(uint hour, uint minute, uint second, uint usecond)
|
||||
:m_hour(hour), m_minute(minute), m_second(second), m_usecond(usecond)
|
||||
{
|
||||
DBUG_ASSERT(is_valid_time_of_day6());
|
||||
}
|
||||
uint hour() const { return m_hour; }
|
||||
uint minute() const { return m_minute; }
|
||||
uint second() const { return m_second; }
|
||||
uint usecond() const { return m_usecond; }
|
||||
/*
|
||||
Return the last time of the day for the given precision, e.g.:
|
||||
- '23:59:59.000000' for decimals==0
|
||||
- '23:59:59.999000' for decimals==3
|
||||
- '23:59:59.999999' for decimals==6
|
||||
*/
|
||||
static TimeOfDay6 end_of_day(decimal_digits_t decimals)
|
||||
{
|
||||
long rem= my_time_fraction_remainder(TIME_MAX_SECOND_PART, decimals);
|
||||
DBUG_ASSERT(rem >= 0 && rem <= TIME_MAX_SECOND_PART);
|
||||
return TimeOfDay6(23, TIME_MAX_MINUTE, TIME_MAX_SECOND,
|
||||
(uint) (TIME_MAX_SECOND_PART - (uint) rem));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SQL_TYPE_TIMEOFDAY_INCLUDED
|
Loading…
x
Reference in New Issue
Block a user