-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DATETIME's behaviour is different from mysql. #11329
Comments
Confirmed that I can still reproduce this against master: select DATE_ADD('1000-01-01 00:00:00',INTERVAL -2 HOUR);
select DATE_ADD('1000-01-01 00:00:00',INTERVAL -200 HOUR);
select DATE_ADD('0001-01-01 00:00:00',INTERVAL -2 HOUR);
select DATE_ADD('0001-01-01 00:00:00',INTERVAL -25 HOUR);
select DATE_ADD('0001-01-01 00:00:00',INTERVAL -8784 HOUR);
select DATE_ADD('0001-01-01 00:00:00',INTERVAL -8785 HOUR);
select tidb_version()\G
..
mysql> select DATE_ADD('1000-01-01 00:00:00',INTERVAL -2 HOUR);
+--------------------------------------------------+
| DATE_ADD('1000-01-01 00:00:00',INTERVAL -2 HOUR) |
+--------------------------------------------------+
| NULL |
+--------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select DATE_ADD('1000-01-01 00:00:00',INTERVAL -200 HOUR);
+----------------------------------------------------+
| DATE_ADD('1000-01-01 00:00:00',INTERVAL -200 HOUR) |
+----------------------------------------------------+
| NULL |
+----------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select DATE_ADD('0001-01-01 00:00:00',INTERVAL -2 HOUR);
+--------------------------------------------------+
| DATE_ADD('0001-01-01 00:00:00',INTERVAL -2 HOUR) |
+--------------------------------------------------+
| NULL |
+--------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select DATE_ADD('0001-01-01 00:00:00',INTERVAL -25 HOUR);
+---------------------------------------------------+
| DATE_ADD('0001-01-01 00:00:00',INTERVAL -25 HOUR) |
+---------------------------------------------------+
| NULL |
+---------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select DATE_ADD('0001-01-01 00:00:00',INTERVAL -8784 HOUR);
+-----------------------------------------------------+
| DATE_ADD('0001-01-01 00:00:00',INTERVAL -8784 HOUR) |
+-----------------------------------------------------+
| NULL |
+-----------------------------------------------------+
1 row in set, 1 warning (0.01 sec)
mysql> select DATE_ADD('0001-01-01 00:00:00',INTERVAL -8785 HOUR);
+-----------------------------------------------------+
| DATE_ADD('0001-01-01 00:00:00',INTERVAL -8785 HOUR) |
+-----------------------------------------------------+
| NULL |
+-----------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select tidb_version()\G
*************************** 1. row ***************************
tidb_version(): Release Version: v4.0.0-beta.2-750-g8a661044c
Edition: Community
Git Commit Hash: 8a661044cedf8daad1de4fbf79a390962b6f6c3b
Git Branch: master
UTC Build Time: 2020-07-10 10:52:37
GoVersion: go1.13
Race Enabled: false
TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306
Check Table Before Drop: false
1 row in set (0.00 sec) |
/pick-up |
It is not a pickable issue! MoreTip : If you want this issue to be picked, you need to add a Warning: None |
The implementation of The definition of /** Daynumber from year 0 to 9999-12-31 */
constexpr const int64_t MAX_DAY_NUMBER = 3652424; the implementation of /**
Add an interval to a MYSQL_TIME struct.
@retval true if error
@retval false otherwise
*/
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
Interval interval, int *warnings) {
ltime->neg = false;
long long sign = (interval.neg ? -1 : 1);
switch (int_type) {
case INTERVAL_SECOND:
case INTERVAL_SECOND_MICROSECOND:
case INTERVAL_MICROSECOND:
case INTERVAL_MINUTE:
case INTERVAL_HOUR:
case INTERVAL_MINUTE_MICROSECOND:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_HOUR_SECOND:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_DAY_SECOND:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_HOUR: {
longlong sec, days, daynr, microseconds, extra_sec;
ltime->time_type = MYSQL_TIMESTAMP_DATETIME; // Return full date
microseconds = ltime->second_part + sign * interval.second_part;
extra_sec = microseconds / 1000000L;
microseconds = microseconds % 1000000L;
if (interval.day > MAX_DAY_NUMBER) goto invalid_date;
if (interval.hour > MAX_DAY_NUMBER * 24ULL) goto invalid_date;
if (interval.minute > MAX_DAY_NUMBER * 24ULL * 60ULL) goto invalid_date;
if (interval.second > MAX_DAY_NUMBER * 24ULL * 60ULL * 60ULL)
goto invalid_date;
sec =
((ltime->day - 1) * 3600LL * 24LL + ltime->hour * 3600LL +
ltime->minute * 60LL + ltime->second +
sign * static_cast<longlong>(
interval.day * 3600ULL * 24ULL + interval.hour * 3600ULL +
interval.minute * 60ULL + interval.second)) +
extra_sec;
if (microseconds < 0) {
microseconds += 1000000LL;
sec--;
}
days = sec / (3600 * 24LL);
sec -= days * 3600 * 24LL;
if (sec < 0) {
days--;
sec += 3600 * 24LL;
}
ltime->second_part = static_cast<uint>(microseconds);
ltime->second = static_cast<uint>(sec % 60);
ltime->minute = static_cast<uint>(sec / 60 % 60);
ltime->hour = static_cast<uint>(sec / 3600);
daynr = calc_daynr(ltime->year, ltime->month, 1) + days;
/* Day number from year 0 to 9999-12-31 */
if (daynr < 0 || daynr > MAX_DAY_NUMBER) goto invalid_date;
get_date_from_daynr(daynr, <ime->year, <ime->month, <ime->day);
break;
}
case INTERVAL_DAY:
case INTERVAL_WEEK: {
unsigned long period;
period = calc_daynr(ltime->year, ltime->month, ltime->day);
if (interval.neg) {
if (period < interval.day) // Before 0.
goto invalid_date;
period -= interval.day;
} else {
if (period + interval.day < period) // Overflow.
goto invalid_date;
if (period + interval.day > MAX_DAY_NUMBER) // After 9999-12-31.
goto invalid_date;
period += interval.day;
}
get_date_from_daynr(period, <ime->year, <ime->month, <ime->day);
} break;
case INTERVAL_YEAR:
if (interval.year > 10000UL) goto invalid_date;
ltime->year += sign * static_cast<long>(interval.year);
if (static_cast<ulong>(ltime->year) >= 10000L) goto invalid_date;
if (ltime->month == 2 && ltime->day == 29 &&
calc_days_in_year(ltime->year) != 366)
ltime->day = 28; // Was leap-year
break;
case INTERVAL_YEAR_MONTH:
case INTERVAL_QUARTER:
case INTERVAL_MONTH: {
unsigned long long period;
// Simple guards against arithmetic overflow when calculating period.
if (interval.month >= UINT_MAX / 2) goto invalid_date;
if (interval.year >= UINT_MAX / 12) goto invalid_date;
period = (ltime->year * 12ULL +
sign * static_cast<unsigned long long>(interval.year) * 12ULL +
ltime->month - 1ULL +
sign * static_cast<unsigned long long>(interval.month));
if (period >= 120000LL) goto invalid_date;
ltime->year = period / 12;
ltime->month = (period % 12L) + 1;
/* Adjust day if the new month doesn't have enough days */
if (ltime->day > days_in_month[ltime->month - 1]) {
ltime->day = days_in_month[ltime->month - 1];
if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
ltime->day++; // Leap-year
}
} break;
default:
fprintf(stderr, "Unexpected interval type: %u\n",
static_cast<unsigned int>(int_type));
assert(false);
goto null_date;
}
return false; // Ok
invalid_date:
if (warnings) {
*warnings |= MYSQL_TIME_WARN_DATETIME_OVERFLOW;
}
null_date:
return true;
} IMO, we can fix this bug be replace Line 135 in 7cd20c0
to MinDatetime = FromDate(0, 0, 0, 0, 0, 0, 0) |
Integrity check: |
Please edit this comment to complete the following informationNot a bug
Duplicate bug
BugNote: Make Sure that 'component', and 'severity' labels are added 1. Root Cause Analysis (RCA)The implementation of date_add_interval is different from MySQL. 2. SymptomThe result for some SQL contains 3. All Trigger ConditionsInput the SQL directly... See the issue describe. 4. Workaround (optional)No. 5. Affected versions[v3.0.0:v3.0.19], [v4.0.0:v4.0.7] 6. Fixed versions4.0.8 |
Please edit this comment to complete the following information
|
Bug Report
Please answer these questions before submitting your issue. Thanks!
If possible, provide a recipe for reproducing the error.
tidb-server -V
or runselect tidb_version();
on TiDB)?The text was updated successfully, but these errors were encountered: