This task is a requirement for MDEV-8894.
Currently MariaDB truncates fractional seconds when changing precision to smaller. This is different from how other databases work. Under terms of this task we'll add a way to do rounding instead of truncation. This will include
- New C++ functions/methods to actually perform rounding for the temporal data types
- A new session sql_mode flag TIME_ROUND_FRACTIONAL to force rounding vs truncation
Note, MDEV-8894 will be fixed by a separate patch after this task.
In the new sql_mode, MariaDB will consistently do all operations (e.g. SET, CAST, ALTER) as follows:
- Round when converting to TIME
- Round when converting to DATETIME and TIMESTAMP
- Truncate when converting to DATE
This is slightly different from how other databases work, but we believe this will give more intuitively expected results in most cases.
MySQL compatibility
The proposed way will give:
- compatible behavior when converting to TIME, DATETIME, TIMESTAMP
- incompatible behavior when converting to DATE
Later, when working on MDEV-8894, we'll introduce a new sql_mode=MYSQL, which will emulate MySQL's rounding behavior precisely.
The current task is to introduce MariaDB's "native" mode for temporal rounding.
Oracle compatibility
Emulating precise Oracle rounding behavior when sql_mode is set to 'ORACLE,TIME_ROUND_FRACTIONAL' is out of scope of this task and will be done separately.
Summary on data type conversion in other databases
- TD - target data type
- SD - source data type
to TIME |
Operation |
TD |
SD |
MySQL |
Oracle |
PostgreSQL |
SQL Standard |
SET |
time(3) |
time(4) |
round |
N/A |
round |
Implementation defined |
SET |
time(3) |
time(4)-in-varchar |
round |
N/A |
error(wrong types) |
error |
SET |
time(3) |
time(7)-in-varchar |
round |
N/A |
error(wrong types) |
error |
SET |
time(3) |
time(4)-in-decimal |
round |
N/A |
error(wrong types) |
error |
SET |
time(3) |
time(7)-in-decimal |
round |
N/A |
error(wrong types) |
error |
ALTER |
time(3) |
time(4) |
round |
N/A |
round |
error |
ALTER |
time(3) |
time(4)-in-varchar |
round |
N/A |
error(wrong types) |
error |
ALTER |
time(3) |
time(7)-in-varchar |
round |
N/A |
error(wrong types) |
error |
ALTER |
time(3) |
time(4)-in-decimal |
round |
N/A |
error(wrong types) |
error |
ALTER |
time(3) |
time(7)-in-decimal |
round |
N/A |
error(wrong types) |
error |
ALTER |
time(6) |
time(7)-in-decimal |
round |
N/A |
error(wrong types) |
error |
CAST |
time(3) |
time(4) literal |
round |
N/A |
round |
Implementation defined |
CAST |
time(3) |
time(4)-in-string |
round |
N/A |
round |
error |
CAST |
time(3) |
time(7)-in-string |
round |
N/A |
round |
error |
CAST |
time(3) |
time(4)-in-decimal |
round |
N/A |
error(wrong types) |
error |
CAST |
time(3) |
time(7)-in-decimal |
round |
N/A |
error(wrong types) |
error |
to TIMESTAMP |
Operation |
TD |
SD |
MySQL |
Oracle |
PostgreSQL |
SQL Standard |
SET |
timestamp(3) |
timestamp(4) |
round |
round |
round |
Implementation defined |
SET |
timestamp(3) |
timestamp(4)-in-varchar |
round |
round |
error(wrong types) |
error |
SET |
timestamp(3) |
timestamp(7)-in-varchar |
round |
round |
error(wrong types) |
error |
SET |
timestamp(3) |
timestamp(4)-in-decimal |
round |
error(wrong types) |
error(wrong types) |
error |
SET |
timestamp(3) |
timestamp(7)-in-decimal |
round |
error(wrong types) |
error(wrong types) |
error |
ALTER |
timestamp(3) |
timestamp(4) |
round |
error |
round |
error |
ALTER |
timestamp(3) |
timestamp(4)-in-varchar |
round |
error(column must be empty) |
error(wrong types) |
error |
ALTER |
timestamp(3) |
timestamp(7)-in-varchar |
round |
error(column must be empty) |
error(wrong types) |
error |
ALTER |
timestamp(3) |
timestamp(4)-in-decimal |
round |
error(column must be empty) |
error(wrong types) |
error |
ALTER |
timestamp(3) |
timestamp(7)-in-decimal |
round |
error(column must be empty) |
error(wrong types) |
error |
CAST |
timestamp(3) |
timestamp(4) literal |
round |
round |
round |
Implementation defined |
CAST |
timestamp(3) |
timestamp(4)-in-string |
round |
round |
round |
error |
CAST |
timestamp(3) |
timestamp(7)-in-string |
round |
round |
round |
error |
CAST |
timestamp(3) |
timestamp(4)-in-decimal |
round |
error(wrong types) |
error(wrong types) |
error |
CAST |
timestamp(3) |
timestamp(7)-in-decimal |
round |
error(wrong types |
error(wrong types) |
error |
to DATE |
Operation |
TD |
SD |
MySQL |
Oracle |
PostgreSQL |
SQL Standard |
SET |
date |
timestamp(4) |
round |
truncate |
truncate |
error |
SET |
date |
timestamp(4)-in-varchar |
round |
error(wrong format) |
error(wrong types) |
error |
SET |
date |
timestamp(7)-in-varchar |
round |
error(wrong format) |
error(wrong types) |
error |
SET |
date |
timestamp(4)-in-decimal |
round |
error(wrong types) |
error(wrong types) |
error |
SET |
date |
timestamp(7)-in-decimal |
round |
error(wrong format) |
error(wrong types) |
error |
ALTER |
date |
timestamp(4) |
round |
round |
truncate |
error |
ALTER |
date |
timestamp(4)-in-varchar |
round |
error(must be empty) |
error(wrong types) |
error |
ALTER |
date |
timestamp(7)-in-varchar |
round |
error(must be empty) |
error(wrong types) |
error |
ALTER |
date |
timestamp(4)-in-decimal |
round |
error(must be empty) |
error(wrong types) |
error |
ALTER |
date |
timestamp(7)-in-decimal |
round |
error(must be empty) |
error(wrong types) |
error |
CAST |
date |
timestamp(4) literal |
truncate |
truncate |
truncate |
truncate |
CAST |
date |
timestamp(4)-in-string |
truncate |
error(wrong format) |
truncate |
error |
CAST |
date |
timestamp(7)-in-string |
round! |
error(wrong format) |
truncate |
error |
CAST |
date |
timestamp(4)-in-decimal |
truncate |
error(wrong types) |
error(wrong types) |
error |
CAST |
date |
timestamp(7)-in-decimal |
round! |
error(wrong types) |
error(wrong types) |
error |
Observations:
- MySQL is not consistent about conversion from varchar to date (reported as MySQL Bug #92475):
- implicit cast always rounds
- explicit CAST truncates for FSP<=6
- explicit CAST rounds for FSP>6
- MySQL: CAST(AS DATE) truncates microseconds but rounds nanoseconds