Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Deliberate, documented changes relative to Connector/C 1.6.0:
| BIT decoding | text: first byte only; binary: little-endian | big-endian value of all bytes (≤ 8) in both protocols |
| BIT parameters | little-endian `bitvalue` encoding | big-endian binary string (matches the decode) |
| `Bool` parameters | fell through to the `MYSQL_TYPE_STRING` fallback (untested latent bug) | bound as `MYSQL_TYPE_TINY` |
| `DateTime` parameters | bound as `MYSQL_TYPE_TIMESTAMP`: MariaDB stored NULL for values before 1970 or after 2106 | bound as `MYSQL_TYPE_DATETIME`, like `Timestamp` |
| TIME decoding | text parse errored on negative/≥24 h; binary ignored sign and days | `Dates.Time` for `0 ≤ t < 24h`, `ConversionError` otherwise; `time_type=Dates.Microsecond` opt-in is lossless and signed |
| Zero dates | text special-cased only zero DATETIME; text zero DATE failed; binary mapped zero components to 1970 | unified `zero_dates` policy: `:sentinel` (default, `Date(0)` / `Timestamp{P}(0, 1, 1)`), `:missing` (widens column types to `Union{Missing, T}`), `:error`; partial zero dates (`2024-00-05`) are `ConversionError` unless `:missing` |
| `Base.isopen` | `mysql_ping` round trip | local check only; use `MySQL.ping(conn)` for a round trip |
Expand Down
4 changes: 3 additions & 1 deletion src/binary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ param_type(::Bit) = return (P.MYSQL_TYPE_BLOB, false)
param_type(::Vector{UInt8}) = return (P.MYSQL_TYPE_BLOB, false)
param_type(::DataBytes) = return (P.MYSQL_TYPE_BLOB, false)
param_type(::Timestamp) = return (P.MYSQL_TYPE_DATETIME, false)
param_type(::DateTime) = return (P.MYSQL_TYPE_TIMESTAMP, false)
# DATETIME, not TIMESTAMP: MariaDB turns a TIMESTAMP-typed parameter outside the TIMESTAMP
# range (before 1970, after 2106) into NULL, even when it is stored in a DATETIME column.
param_type(::DateTime) = return (P.MYSQL_TYPE_DATETIME, false)
param_type(::Date) = return (P.MYSQL_TYPE_DATE, false)
param_type(::Dates.Time) = return (P.MYSQL_TYPE_TIME, false)
param_type(::AbstractString) = return (P.MYSQL_TYPE_STRING, false)
Expand Down
3 changes: 2 additions & 1 deletion test/protocol/binary_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,8 @@ end
@test roundtrip(Timestamp{Microsecond}, Timestamp{Nanosecond}(2024, 1, 2, 1, 2, 3, 456, 789)) === Timestamp{Microsecond}(2024, 1, 2, 1, 2, 3, 456, 789)
# the wire carries microseconds: a sub-microsecond Timestamp{Nanosecond} is refused, never truncated
@test_throws InexactError N.encode_param_value!(UInt8[], Timestamp{Nanosecond}(2024, 1, 2, 1, 2, 3, 456, 789, 1))
@test N.param_signature(Any[Timestamp{Second}(2024, 1, 1), DateTime(2024, 1, 1)]) == UInt16[P.MYSQL_TYPE_DATETIME, P.MYSQL_TYPE_TIMESTAMP]
# DateTime binds as DATETIME: MariaDB nulls TIMESTAMP-typed parameters outside 1970-2106
@test N.param_signature(Any[Timestamp{Second}(2024, 1, 1), DateTime(2024, 1, 1)]) == UInt16[P.MYSQL_TYPE_DATETIME, P.MYSQL_TYPE_DATETIME]
@test roundtrip(Time, Time(13, 14, 15)) == Time(13, 14, 15)
@test roundtrip(Time, Time(13, 14, 15, 250, 500)) == Time(13, 14, 15, 250, 500)

Expand Down
10 changes: 10 additions & 0 deletions test/user_workflow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ function run_user_workflow(conn)
@test expected.data == [UInt8[0, 255], UInt8[2], UInt8[3]]
@test Tables.columntable(DBInterface.execute(conn, "SELECT * FROM items ORDER BY id"; mysql_store_result=false)) == expected
@test Tables.columntable(DBInterface.execute(conn, "SELECT * FROM items WHERE id > ? ORDER BY id", (0,))) == expected
# DateTime parameters before 1970 or after 2106: MariaDB stored NULL for them while they
# were sent as MYSQL_TYPE_TIMESTAMP.
DBInterface.execute(conn, "DROP TABLE IF EXISTS old_dates")
DBInterface.execute(conn, "CREATE TABLE old_dates (id INT PRIMARY KEY, at DATETIME(3))")
dates = [DateTime(1955, 3, 4, 5, 6, 7, 890), DateTime(1969, 12, 31, 23, 59, 59), DateTime(2200, 1, 1)]
DBInterface.executemany(conn, "INSERT INTO old_dates VALUES (?, ?)", (id=[1, 2, 3], at=dates))
@test isequal(Tables.columntable(DBInterface.execute(conn, "SELECT at FROM old_dates ORDER BY id")).at, Timestamp{Millisecond}.(dates))
DBInterface.execute(conn, "DROP TABLE IF EXISTS old_dates_load")
MySQL.load((id=[1], born=[DateTime(1955, 3, 4)]), conn, "old_dates_load")
@test isequal(Tables.columntable(DBInterface.execute(conn, "SELECT born FROM old_dates_load")).born, [Timestamp{Millisecond}(DateTime(1955, 3, 4))])
@test DBInterface.transaction(conn) do
DBInterface.execute(conn, "UPDATE items SET label='changed' WHERE id=1")
:committed
Expand Down
Loading