WL#16790: Refactor TIME handling in server
Affects: Server-9.x
—
Status: Complete
The server operates with values in MYSQL_TIME which can be either of time, date and datetime. This is impractical for consistency and efficiency reasons. Consistency is affected because we have one data type that represents three different values. Efficiency is affected because in order to operate efficiently, we need to convert the generic temporal values into more efficient integer values. This will be replaced by a new Time_val class that only takes 8 byte of storage and bit-codes the various components of a time value for simplicity and efficiency. Types DATE, DATETIME and TIMESTAMP are not affected by this work. Interfaces to Field and Item classes are also simplified. Goals of this work: - Better type safety - Simpler implementation (except for some specific cases - Less error prone, due to simplicity - Efficient comparison operations (map to integer operations) - Efficient move operations due to smaller size - Efficient move operations due to simpler conversions - Efficiency through inlining
This is mostly refactoring. Apart from some increased consistency,
there are no functional requirements in this worklog.
NFR-1: Time handling should be at least as efficient as previous handling.
Focus is on efficiency for most common operations: storage, retrieval,
movement and comparison, and less on arithmetic operations.
Performance will to some extent be validated in unit tests.
See Low Level Design for actual changes. BUG#117138 has been corrected with the new TIME datatype evaluation functions. Microbenchmark results, times per iteration, : BM_val_time: Reduced from 7 ns to 4 ns. BM_store_time: Reduced from 31 ns to 17 ns. BM_add_time_interval: Reduced from 114 ns to 54 ns.
Class Time_val:
==============
The range of TIME values is from -838:59:59.000000 to 838:59:59.000000
One time value is stored as an unsigned 8 byte value (2 bytes are unused).
For simple implementation of operations, all time values are extended to
six digits of precision. This is in contrast to the storage engine interface
which provides 4 formats: 0 digits, 1-2 digits, 3-4 digits, 5-6 digits.
The time components are bit-coded as follows within 48 bits:
- sign 1 bit (47-47)
- hours 11 bits (36-46)
- minutes 6 bits (30-35)
- seconds 6 bits (24-29)
- microseconds 24 bits ( 0-23)
The sign bit is 1 for positive values, and 0 for negative values.
Negative values are stored with each bit field as the one's complement of
the corresponding positive value.
The negative zero value -00:00:00.000000 is stored like positive zero,
thus we do not represent this value internally.
This format is easy to convert to and from the storage engine interface.
It is also directly comparable as an integer value.
Finally, it is easy to extract time components directly from the format.
A time value is always valid. All constructors and mutator functions validate
that the result is correct with asserts in debug mode.
The interfaces to class Time_val are as follows:
- Constructor from component values (hour, minute, second, fraction, sign).
- Constructor from seconds and microseconds.
- Constructor from MYSQL_TIME struct
- Constructor from integer value (private)
- bool is_negative()
- uint32_t hour()
- uint32_t minute()
- uint32_t second()
- uint32_t microsecond()
- int compare(const Time_val arg)
Compares two time values and returns -1, 0 or 1 if current value is
less than, equal to or greater than the other.
- longlong for_comparison()
Returns an integer value that can be used for efficient (integer) comparison.
- void set_zero()
Set the time value to zero values.
- void set_extreme_value(bool positive)
Set the time value to lowest or highest possible values.
- bool operator==(const Time_val rhs)
Operator for use in std:: functions.
- bool add(Time_val tv, bool subtract)
Add or subtract a time value to/from the current time value.
- bool add(Interval &iv, bool subtract)
Add or subtract an interval to/from the current time value.
- Time_val strip_date()
Contruct a time value from MYSQL_TIME, and strip off date component.
- int32_t to_seconds()
Return the number of seconds in a time value.
- int64_t to_microseconds()
Return the number of microseconds in a time value.
- int64_t to_int_rounded()
Convert time value to HHMMSS format and return it as an integer.
Microsecond component is rounded to nearest second value.
- int64_t to_int_truncated()
Convert time value to HHMMSS format and return it as an integer.
Microsecond component is truncated away.
- double to_double()
Convert time value to HHMMSS.ffffff format and return it as float value.
- is_adjusted()
Returns true if fraction of time value is adjusted to the supplied
number of digits.
- uint actual_decimals()
Returns the number of digits in the time value fraction, not counting
trailing zero digits.
- bool adjust_fraction()
Adjust fraction to supplied number of digits, with rounding or truncation.
- size_t to_string()
Format time value as a string.
- load_time()
Load time value from storage engine format (binary format).
- store_time()
Store time value into storage engine format (binary format).
- operator MYSQL_TIME()
Convert time value to MYSQL_TIME format.
- bool make_time()
Construct a time value, return true if values are out of range.
Changes to class Field
----------------------
- Interface function get_time() is renamed to val_time() and returns a
Time_val value.
- Function store_packed() is renamed as store_time() and takes a Time_val
argument.
- Interface val_time_temporal() has been eliminated. All uses are replaced
with calls to val_time() followed by for_comparison().
- Interface val_time_temporal_at_utc() has also been eliminated.
"at UTC" has no impact for a TIME value.
- Interface val_temporal_by_field_type() has been replaced with
val_date_temporal. It was used to choose between val_time_temporal()
and val_date_temporal(), but since the former is gone, it is no longer
necessary.
Changes to class Item
---------------------
- All interfaces get_time_from_xxx() have been updated to return a Time_val.
- Interface function get_time() is renamed to val_time() and returns a
Time_val value.
- Interface val_time_temporal() has been eliminated. All uses now call
val_time() followed by for_comparison().
- Interface val_time_temporal_at_utc() has also been eliminated.
"at UTC" has no impact for a TIME value.
- Interface val_temporal_by_field_type() has been replaced with
val_date_temporal. It was used to choose between val_time_temporal()
and val_date_temporal(), but since the former is gone, it is no longer
necessary.
- Function int_sort_key() no longer calls val_time_temporal_at_utc().
Instead it calls val_time() and for_comparison(), which returns a value
that can be compared using integer functions.
Changes to class Item_time_literal
----------------------------------
This class now stores the const value in a Time_val object instead of
MYSQL_TIME.
Changes to class Item_cache_time
--------------------------------
This is a new class that replaces class Item_cache_datetime for TIME values.
- It implements all the usual Item_cache interface functions.
- It stores a value in a private Time_val member.
- For simplicity, it does not implement a cached string value.
It is also rarely needed, as TIME values are most commonly used with other
TIME values.
Changes to class Item_time_with_ref
-----------------------------------
This class was used to store const values of type TIME, used in comparison
operations. Its use has been replaced with Item_time_literal, and the class
has been deleted.
Changes to class Cached_item_time
---------------------------------
This is a new class that replaces class Cached_item_temporal for TIME values.
- It implements all the usual Cached_item interface functions.
- It stores a value in a private Time_val member.
Changes to class Arg_comparator
-------------------------------
Function compare_time_packed() has been renamed to compare_time().
It now operates on Time_val values, instead of values fetched using
val_time_temporal().
Changes to class Item_func_add_time
-----------------------------------
- Member m_sign is replaced with m_subtract.
- During evaluation, it will now do a more thorough analysis of supplied
string arguments, giving a better possibility of returning useful results.
- A second argument that is a datetime expression is now implicitly
converted to a TIME value.
- If first argument is a DATE and second argument is a TIME, the result
is now a DATETIME.
Changes to histogram handling
-----------------------------
Time values are now stored using the Time_val struct.
Templatized classes have been added as needed.
Changes to class Json_dom
-------------------------
The value class hierarchy has been extended. There used to be a class
Json_datetime that managed all temporal values. It has been replaced with
an abstract class Json_temporal that has two subclasses Json_time and
Json_datetime. Json_time stores its value in a Time_val object.
Changes to JSON time handling
-----------------------------
- Json_time::from_packed(), Json_time::to_packed(),
Json_time::from_packed_to_key() and Json_wrapper::get_time_packed()
are new functions used to manipulate TIME values in JSON functions.
- time_to_json_storage() has been renamed from TIME_to_longlong_time_packed()
and moved from my_time.cc into json_dom.cc, since it is now only used
to generate stored JSON time values.
- time_from_json_storage() has been renamed from
TIME_from_longlong_time_packed() and moved from my_time.cc into json_dom.cc,
similar to the above.
Changes to general time handling functions
------------------------------------------
Some functions now operate on a Time_val struct instead of a MYSQL_TIME struct.
- my_time_packed_to_binary()
this is moved to class Time_val and renamed as store_time()
- my_time_packed_from_binary()
this is moved to class Time_val and renamed as load_time()
- TIME_to_longlong_time_packed()
renamed to time_to_jtime() (This becomes internal JSON storage format).
Reimplemented using Time_val instead of MYSQL_TIME.
The implementation is also moved to rpd_mytime.cc for use in Rapid.
- TIME_from_longlong_time_packed()
renamed to time_from_jtime() (This becomes internal JSON storage format).
Reimplemented using Time_val instead of MYSQL_TIME.
The implementation is also moved to rpd_mytime.cc for use in Rapid.
Changes to time handling in RAPID
---------------------------------
Handling in RAPID has been changed as little as possible.
Some functions that have been obsoleted in the server have been moved
into RAPID (rpd_mytime.cc).
- Function my_time_packed_from_binary() has been replaced by
Time_val::load_time(), followed by TIME_to_longlong_time_packed()
to convert TIME value to RAPID internal format.
- Literal TIME values in Item_temporal have been replaced with
Item_time_literal.
UNIT testing of TIME functions
------------------------------
- Unit testing for class Field, class Item and histograms have been changed
to use the new Time_val class.
- Microbenchmarks to measure efficiency of some important TIME functions
have been added:
BM_val_time - evaluate a TIME value from a field.
BM_add_time_interval - add intervals to a TIME value.
- A new unit test file temporal-t.cc containing unit tests for class Time_val
has been added.
Changes to test results
-----------------------
- Because of change from Item_temporal to Item_time_literal, some literal TIME
values are now displayed with the correct TIME prefix.
- main.func_date_add: ADDTIME with a DATE and a TIME argument has been
corrected to return a DATETIME result. The first argument can be a TIME or
a DATETIME expression, and in case of a DATE, it is extended to a DATETIME.
There is a similar change in main.sp-bugs.
- main.time_zone: ADDTIME where the second argument is a datetime. This
argument is now implicitly truncated to a TIME value, and a valid result
is returned instead of NULL. See also main.sapdb.
- main.type_temporal_fractional: WEIGHT_STRING for a temporal value is now
calculated differently. It is however unsure what the use of this would be.
- json.json_functions_innodb: A returned value is changed from
630:59:59.000000 to 838:59:59.000000. This corrects bug#37458212.
Copyright (c) 2000, 2026, Oracle Corporation and/or its affiliates. All rights reserved.