Skip to content

Commit 6b5b6f5

Browse files
committed
WL#16790: Refactor TIME handling in server
This patch changes internal handling of TIME values in the MySQL server from using the MYSQL_TIME struct to use the new class Time_val. Class Time_val is designed to be small and efficient, and to improve maintainability of temporal types in the server. See Low Level Design of the worklog for details. Change-Id: I2ae6cc79cf25d6a4ce995e1b876049e103beb78e
1 parent c0732c5 commit 6b5b6f5

File tree

94 files changed

+5474
-3375
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+5474
-3375
lines changed

client/json_client_library_main.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ int main() {
320320
{
321321
Json_wrapper jw = Json_wrapper(
322322
create_dom_ptr<Json_string>("2015-01-15 23:24:25.000000"));
323-
MYSQL_TIME ltime;
323+
Time_val ltime;
324324
bool res = jw.coerce_time(
325325
[](const char *, int) { std::cout << "9.6. ERROR \n"; },
326326
[](MYSQL_TIME_STATUS &) { std::cout << "9.6. checking \n"; }, &ltime);

include/my_temporal.h

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
#ifndef MY_TEMPORAL_H
2+
#define MY_TEMPORAL_H
3+
4+
/* Copyright (c) 2025, Oracle and/or its affiliates.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License, version 2.0,
8+
as published by the Free Software Foundation.
9+
10+
This program is also distributed with certain software (including
11+
but not limited to OpenSSL) that is licensed under separate terms,
12+
as designated in a particular file or component or in included license
13+
documentation. The authors of MySQL hereby grant you an additional
14+
permission to link the program and your derivative works with the
15+
separately licensed software that they have included with MySQL.
16+
17+
This program is distributed in the hope that it will be useful,
18+
but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
GNU General Public License, version 2.0, for more details.
21+
22+
You should have received a copy of the GNU General Public License
23+
along with this program; if not, write to the Free Software
24+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
25+
26+
/**
27+
@ingroup MY_TEMPORAL
28+
@{
29+
30+
@file include/my_temporal.h
31+
32+
Server classes for temporal handling (DATE, TIME, DATETIME)
33+
*/
34+
35+
class Time_val;
36+
struct Interval;
37+
38+
#include "my_time.h"
39+
#include "mysql_time.h"
40+
41+
#include <sys/types.h>
42+
#include <cassert>
43+
#include <iostream>
44+
#include <limits>
45+
46+
/**
47+
Time_val is a temporal type that represents only time.
48+
It has constructors for creating time values from time components
49+
(hour, minute, second and microseconds), and from seconds and microseconds.
50+
It also has a constructor to create a value from a MYSQL_TIME value.
51+
52+
The range of values supported is from -838:59:59 to +838:59:59.
53+
The negative values, and the values from 24:00:00 and up are dedicated
54+
for use as a small-range interval type and should not be taken as a time
55+
within a day.
56+
*/
57+
class Time_val {
58+
public:
59+
Time_val() : m_value() {}
60+
61+
Time_val(bool negative, uint32_t hour, uint32_t minute, uint32_t second,
62+
uint32_t microsecond) {
63+
assert(hour <= TIME_MAX_HOUR && minute <= TIME_MAX_MINUTE &&
64+
second <= TIME_MAX_SECOND && microsecond <= TIME_MAX_MICROSEC);
65+
m_value = (static_cast<uint64_t>(hour) << TIME_SHIFT_HOUR) |
66+
(static_cast<uint64_t>(minute) << TIME_SHIFT_MINUTE) |
67+
(static_cast<uint64_t>(second) << TIME_SHIFT_SECOND) |
68+
microsecond;
69+
if (negative) {
70+
assert(hour != 0 || minute != 0 || second != 0 || microsecond != 0);
71+
// Stored as one's complement, negate value and subtract one
72+
m_value = (static_cast<uint64_t>(-static_cast<int64_t>(m_value)) &
73+
(BITS_MICROSEC | BITS_SECOND | BITS_MINUTE | BITS_HOUR)) -
74+
1;
75+
} else {
76+
m_value |= BITS_SIGN;
77+
}
78+
assert(is_valid());
79+
}
80+
81+
Time_val(bool negative, uint32_t second, uint32_t microsecond)
82+
: Time_val(negative, second / SECS_PER_HOUR,
83+
(second / SECS_PER_MIN) % MINS_PER_HOUR, second % SECS_PER_MIN,
84+
microsecond) {}
85+
86+
explicit Time_val(const MYSQL_TIME &mtime)
87+
: Time_val(mtime.neg, mtime.hour, mtime.minute, mtime.second,
88+
mtime.second_part) {
89+
assert(mtime.time_type == MYSQL_TIMESTAMP_TIME);
90+
}
91+
92+
bool is_negative() const { return (m_value & BITS_SIGN) == 0; }
93+
uint32_t hour() const {
94+
return is_negative() ? 2047U - ((m_value & BITS_HOUR) >> TIME_SHIFT_HOUR)
95+
: (m_value & BITS_HOUR) >> TIME_SHIFT_HOUR;
96+
}
97+
uint32_t minute() const {
98+
return is_negative() ? 63U - ((m_value & BITS_MINUTE) >> TIME_SHIFT_MINUTE)
99+
: (m_value & BITS_MINUTE) >> TIME_SHIFT_MINUTE;
100+
}
101+
uint32_t second() const {
102+
return is_negative() ? 63U - ((m_value & BITS_SECOND) >> TIME_SHIFT_SECOND)
103+
: (m_value & BITS_SECOND) >> TIME_SHIFT_SECOND;
104+
}
105+
uint32_t microsecond() const {
106+
return is_negative() ? BITS_MICROSEC - (m_value & BITS_MICROSEC)
107+
: (m_value & BITS_MICROSEC);
108+
}
109+
110+
/**
111+
@returns whether the value is less than, equal to or greater than
112+
the argument value.
113+
*/
114+
int compare(const Time_val arg) const {
115+
return m_value < arg.m_value ? -1 : m_value > arg.m_value ? 1 : 0;
116+
}
117+
/// @returns an integer value for comparison purposes
118+
int64_t for_comparison() const { return m_value; }
119+
120+
/// Check against extreme values
121+
bool is_extreme_value(bool positive) const {
122+
return m_value == (positive ? MAX_TIME_VALUE : MIN_TIME_VALUE);
123+
}
124+
125+
/// Set zero time
126+
void set_zero() { m_value = BITS_SIGN; }
127+
128+
/// Set extreme value
129+
void set_extreme_value(bool negative) {
130+
m_value = negative ? MIN_TIME_VALUE : MAX_TIME_VALUE;
131+
}
132+
133+
bool operator==(const Time_val rhs) const { return m_value == rhs.m_value; }
134+
135+
/**
136+
Add a time value to another time value, or subtract it
137+
138+
@param tv Time value to add or subtract
139+
@param subtract If true, subtract the time value, otherwise add it.
140+
141+
@returns false if result is within valid time range, true otherwise.
142+
*/
143+
bool add(Time_val tv, bool subtract);
144+
145+
/**
146+
Add an interval to a time value, or subtract it
147+
148+
@param iv Interval to add or subtract
149+
@param subtract If true, subtract the time value, otherwise add it.
150+
151+
@returns false if result is within valid time range, true otherwise.
152+
*/
153+
bool add(Interval &iv, bool subtract);
154+
155+
/// Static functions for creation
156+
/**
157+
Creates Time_val with range check. Minute, second and microsecond
158+
values must be within limits.
159+
160+
@returns false if valid range, true if outside valid range.
161+
*/
162+
static bool make_time(bool negative, uint32_t hour, uint32_t minute,
163+
uint32_t second, uint32_t microsecond, Time_val *time) {
164+
if (hour > TIME_MAX_HOUR || (hour == TIME_MAX_HOUR && microsecond != 0)) {
165+
return true;
166+
}
167+
assert(minute <= TIME_MAX_MINUTE && second <= TIME_MAX_SECOND &&
168+
microsecond <= TIME_MAX_MICROSEC);
169+
*time = Time_val(negative, hour, minute, second, microsecond);
170+
return false;
171+
}
172+
/// Creates a Time_val from a date_time by extracting only the time fields
173+
static Time_val strip_date(const MYSQL_TIME &mt);
174+
175+
/// Convert time value to the generalized temporal time format.
176+
explicit operator MYSQL_TIME() const;
177+
178+
/// @returns time value as number of seconds. Fraction seconds are ignored.
179+
int32_t to_seconds() const {
180+
return static_cast<int32_t>(unsigned_seconds(hour(), minute(), second())) *
181+
(is_negative() ? -1 : 1);
182+
}
183+
/// @returns time value as number of microseconds.
184+
int64_t to_microseconds() const {
185+
return static_cast<int64_t>(
186+
unsigned_microsec(hour(), minute(), second(), microsecond())) *
187+
(is_negative() ? -1 : 1);
188+
}
189+
/**
190+
base100 representation without microsecond, but rounded
191+
'-12:34:56.999999' is returned as -123457
192+
*/
193+
int64_t to_int_rounded() const;
194+
/**
195+
base100 representation without microsecond, '-12:34:56.999999' is
196+
returned as -123456
197+
*/
198+
int64_t to_int_truncated() const;
199+
/**
200+
base100 representation with microseconds, returned as double precision float
201+
*/
202+
double to_double() const;
203+
204+
/// @returns true if value is adjusted to number of decimals in fraction
205+
bool is_adjusted(uint32_t decimals) const;
206+
207+
/// @returns actual number of decimals in fraction
208+
uint32_t actual_decimals() const;
209+
210+
// Mutators
211+
void adjust_fraction(uint32_t decimals, bool round);
212+
213+
// Add nanoseconds to a time value, with rounding
214+
bool add_nanoseconds_round(const int64_t nanoseconds) {
215+
return nanoseconds < 0 ? add_microseconds((nanoseconds - 500) / 1000)
216+
: add_microseconds((nanoseconds + 500) / 1000);
217+
}
218+
219+
/**
220+
Convert server time value to storage engine interface format
221+
222+
@param [out] ptr The buffer to put value at.
223+
@param dec Precision.
224+
*/
225+
void store_time(uint8_t *ptr, uint32_t dec) const;
226+
227+
/**
228+
Convert from storage engine interface time format to server time value.
229+
230+
@param ptr The pointer to read the value at.
231+
@param dec Precision.
232+
@param[out] time Returned time value
233+
*/
234+
static void load_time(const uint8_t *ptr, uint32_t dec, Time_val *time);
235+
236+
size_t to_string(char *buffer, uint32_t dec) const;
237+
238+
std::string to_string() const;
239+
240+
private:
241+
explicit Time_val(int64_t val) : m_value(val) {}
242+
243+
/// Set microsecond part of time value
244+
void set_microsecond(uint32_t fraction) {
245+
m_value = (m_value & ~BITS_MICROSEC) |
246+
(is_negative() ? (0xFFFFFFU - fraction) : fraction);
247+
}
248+
249+
bool add_seconds(int32_t seconds) {
250+
Time_val tv(seconds < 0, 0, 0, static_cast<uint8_t>(abs(seconds)), 0);
251+
add(tv, false);
252+
assert(is_valid());
253+
return false;
254+
}
255+
256+
bool add_microseconds(int64_t mu) {
257+
int64_t signed_micro =
258+
unsigned_microsec(hour(), minute(), second(), microsecond());
259+
if (is_negative()) signed_micro = -signed_micro;
260+
signed_micro += mu;
261+
bool negative = signed_micro < 0;
262+
uint64_t micro = negative ? -signed_micro : signed_micro;
263+
if (micro > MAX_TIME_MICROSEC) {
264+
return true;
265+
}
266+
uint32_t seconds = static_cast<uint32_t>(micro / TIME_MULT_SECOND);
267+
micro %= TIME_MULT_SECOND;
268+
*this = Time_val(negative, seconds, micro);
269+
assert(is_valid());
270+
return false;
271+
}
272+
273+
bool is_valid() const {
274+
return hour() <= TIME_MAX_HOUR && minute() <= TIME_MAX_MINUTE &&
275+
second() <= TIME_MAX_SECOND && microsecond() <= TIME_MAX_MICROSEC &&
276+
m_value <= MAX_TIME_VALUE && m_value >= MIN_TIME_VALUE;
277+
}
278+
279+
static uint32_t unsigned_seconds(uint32_t hour, uint32_t minute,
280+
uint32_t second) {
281+
return (hour * SECS_PER_HOUR) + (minute * SECS_PER_MIN) + second;
282+
}
283+
284+
static uint64_t unsigned_microsec(uint32_t hour, uint32_t minute,
285+
uint32_t second, uint32_t microsec) {
286+
return (hour * TIME_MULT_HOUR) + (minute * TIME_MULT_MINUTE) +
287+
(second * TIME_MULT_SECOND) + microsec;
288+
}
289+
290+
static constexpr const uint32_t TIME_MAX_HOUR = 838;
291+
static constexpr const uint32_t TIME_MAX_MINUTE = 59;
292+
static constexpr const uint32_t TIME_MAX_SECOND = 59;
293+
static constexpr const uint32_t TIME_MAX_MICROSEC = 999999;
294+
295+
static constexpr uint64_t TIME_MULT_SECOND = 1000000;
296+
static constexpr uint64_t TIME_MULT_MINUTE = 60000000;
297+
static constexpr uint64_t TIME_MULT_HOUR = 3600000000;
298+
299+
static constexpr uint64_t BITS_MICROSEC = 0x0000000000FFFFFF;
300+
static constexpr uint64_t BITS_SECOND = 0x000000003F000000;
301+
static constexpr uint64_t BITS_MINUTE = 0x0000000FC0000000;
302+
static constexpr uint64_t BITS_HOUR = 0x00007FF000000000;
303+
static constexpr uint64_t BITS_SIGN = 0x0000800000000000;
304+
static constexpr int TIME_SHIFT_SECOND = 24;
305+
static constexpr int TIME_SHIFT_MINUTE = 30;
306+
static constexpr int TIME_SHIFT_HOUR = 36;
307+
308+
static constexpr uint64_t MAX_TIME_VALUE =
309+
BITS_SIGN | (static_cast<uint64_t>(TIME_MAX_HOUR) << TIME_SHIFT_HOUR) |
310+
(static_cast<uint64_t>(TIME_MAX_MINUTE) << TIME_SHIFT_MINUTE) |
311+
(static_cast<uint64_t>(TIME_MAX_SECOND) << TIME_SHIFT_SECOND);
312+
313+
static constexpr uint64_t MIN_TIME_VALUE =
314+
(static_cast<uint64_t>(-static_cast<int64_t>(MAX_TIME_VALUE)) &
315+
(BITS_MICROSEC | BITS_SECOND | BITS_MINUTE | BITS_HOUR)) -
316+
1;
317+
318+
// 838:59:59.000000
319+
static constexpr uint64_t MAX_TIME_MICROSEC =
320+
(TIME_MAX_HOUR * TIME_MULT_HOUR) + (TIME_MAX_MINUTE * TIME_MULT_MINUTE) +
321+
(TIME_MAX_SECOND * TIME_MULT_SECOND);
322+
323+
/**
324+
A TIME value is stored in bit coded fields in a 64 bit unsigned value.
325+
The format is efficient for comparison, storage, retrieval and movement.
326+
The fields are stored in two's complement, but with a sign bit set for
327+
non-negative values. This means that values can be compared using regular
328+
unsigned integer logic.
329+
Format:
330+
Bits 0-23: microseconds (0-999999)
331+
Bits 24-29: seconds (0-59)
332+
Bits 30-35: minutes (0-59)
333+
Bits 36-46: hours (0-838) (Theoretical range up to 2047)
334+
Bits 47-47: Sign (1 for positive value, 0 for negative value)
335+
*/
336+
uint64_t m_value = 0xffffffffffffffff;
337+
};
338+
339+
/**
340+
@} (end of ingroup MY_TEMPORAL)
341+
*/
342+
#endif // MY_TEMPORAL_H

include/my_time.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,9 @@ inline long long int my_packed_time_get_frac_part(long long int i) {
365365
long long int year_to_longlong_datetime_packed(long year);
366366
long long int TIME_to_longlong_datetime_packed(const MYSQL_TIME &my_time);
367367
long long int TIME_to_longlong_date_packed(const MYSQL_TIME &my_time);
368-
long long int TIME_to_longlong_time_packed(const MYSQL_TIME &my_time);
369368
long long int TIME_to_longlong_packed(const MYSQL_TIME &my_time);
370369

371370
void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, long long int nr);
372-
void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, long long int nr);
373371
void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, long long int nr);
374372
void TIME_set_yymmdd(MYSQL_TIME *ltime, unsigned int yymmdd);
375373
void TIME_set_hhmmss(MYSQL_TIME *ltime, unsigned int hhmmss);
@@ -379,11 +377,6 @@ void my_datetime_packed_to_binary(long long int nr, unsigned char *ptr,
379377
long long int my_datetime_packed_from_binary(const unsigned char *ptr,
380378
unsigned int dec);
381379

382-
void my_time_packed_to_binary(long long int nr, unsigned char *ptr,
383-
unsigned int dec);
384-
long long int my_time_packed_from_binary(const unsigned char *ptr,
385-
unsigned int dec);
386-
387380
void my_timestamp_to_binary(const my_timeval *tm, unsigned char *ptr,
388381
unsigned int dec);
389382
void my_timestamp_from_binary(my_timeval *tm, const unsigned char *ptr,

mysql-test/r/date_formats.result

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ f1 f2 f3
428428
Warnings:
429429
Warning 1292 Truncated incorrect datetime value: '2003-01-02 10:11:12.0012ABCD'
430430
Warning 1292 Truncated incorrect time value: '-01:01:01.01 GGG'
431-
Warning 4096 Delimiter ' ' in position 12 in datetime value '-01:01:01.01 GGG' at row 1 is superfluous and is deprecated. Please remove.
432431
Warning 1292 Truncated incorrect time value: '1997-12-31 23:59:59.01XXXX'
433432
select str_to_date("2003-04-05 g", "%Y-%m-%d") as f1,
434433
str_to_date("2003-04-05 10:11:12.101010234567", "%Y-%m-%d %H:%i:%S.%f") as f2;

0 commit comments

Comments
 (0)