Skip to content

Commit 5e45c84

Browse files
authored
Convert non-binary columns to String type (#153)
* Convert non-binary columns to String type Fixes #150. The issue here is that the C API, by default, says that TEXT columns are of type MYSQL_TYPE_BLOB, from what I gathered because C doesn't really have a true "string" type, so it doesn't really matter if it's blob or string anyway. But, upon further investigation, I found in the MYSQL_FIELD flags field documentation a bit switch to tell whether a column is in fact binary or not. Using this, we can now detect accurately whether a column can be treated as a string or should be returned as a byte vector. The tests added test the return value of columns defined as TEXT and BLOB and that they are accurately distinguished on the Julia side. * fix failing test
1 parent e71e3a1 commit 5e45c84

File tree

6 files changed

+25
-4
lines changed

6 files changed

+25
-4
lines changed

src/MySQL.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,11 @@ end
260260
Base.close(conn::Connection) = DBInterface.close!(conn)
261261
Base.isopen(conn::Connection) = API.isopen(conn.mysql)
262262

263-
function juliatype(field_type, notnullable, isunsigned)
263+
function juliatype(field_type, notnullable, isunsigned, isbinary)
264264
T = API.juliatype(field_type)
265265
T2 = isunsigned ? unsigned(T) : T
266-
return notnullable ? T2 : Union{Missing, T2}
266+
T3 = !isbinary && T2 == Vector{UInt8} ? String : T2
267+
return notnullable ? T3 : Union{Missing, T3}
267268
end
268269

269270
include("execute.jl")

src/api/apitypes.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct MYSQL_FIELD
9090
end
9191
notnullable(field) = (field.flags & NOT_NULL_FLAG) > 0
9292
isunsigned(field) = (field.flags & UNSIGNED_FLAG) > 0
93+
isbinary(field) = (field.flags & BINARY_FLAG) > 0
9394

9495
const MYSQL_FIELD_OFFSET = Cuint
9596
const MYSQL_ROW = Ptr{Ptr{UInt8}}

src/api/consts.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ const MYSQL_TIMESTAMP_TIME = 2
205205

206206
const NOT_NULL_FLAG = UInt32(1)
207207
const UNSIGNED_FLAG = UInt32(32)
208+
const BINARY_FLAG = UInt32(128)
208209
const MYSQL_NO_DATA = 100
209210

210211
const MYSQL_DEFAULT_PORT = 3306

src/execute.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ function DBInterface.execute(conn::Connection, sql::AbstractString, params=(); m
152152
nfields = API.numfields(result)
153153
fields = API.fetchfields(result, nfields)
154154
names = [ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Csize_t), x.name, x.name_length) for x in fields]
155-
types = [juliatype(x.field_type, API.notnullable(x), API.isunsigned(x)) for x in fields]
155+
types = [juliatype(x.field_type, API.notnullable(x), API.isunsigned(x), API.isbinary(x)) for x in fields]
156156
elseif API.fieldcount(conn.mysql) == 0
157157
rows_affected = API.affectedrows(conn.mysql)
158158
names = Symbol[]

src/prepare.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function DBInterface.prepare(conn::Connection, sql::AbstractString)
5151
if result.ptr != C_NULL
5252
fields = API.fetchfields(result, nfields)
5353
names = [ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Csize_t), x.name, x.name_length) for x in fields]
54-
types = [juliatype(x.field_type, API.notnullable(x), API.isunsigned(x)) for x in fields]
54+
types = [juliatype(x.field_type, API.notnullable(x), API.isunsigned(x), API.isbinary(x)) for x in fields]
5555
valuehelpers = [API.BindHelper() for i = 1:nfields]
5656
values = [API.MYSQL_BIND(valuehelpers[i].length, valuehelpers[i].is_null) for i = 1:nfields]
5757
foreach(1:nfields) do i

test/runtests.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,21 @@ DBInterface.execute(stmt, -1)
203203
res = DBInterface.execute(conn, "select id from negative_int") |> columntable
204204
@test length(res) == 1
205205
@test res[1][1] === Int32(-1)
206+
207+
208+
DBInterface.execute(conn, "DROP TABLE if exists text_field")
209+
DBInterface.execute(conn, "CREATE TABLE text_field (id int(11), t text)")
210+
stmt = DBInterface.prepare(conn, "INSERT INTO text_field (id, t) VALUES (?, ?);")
211+
DBInterface.execute(stmt, [-1, "hey there sailor"])
212+
res = DBInterface.execute(conn, "select id, t from text_field") |> columntable
213+
@test length(res) == 2
214+
@test res[2][1] === "hey there sailor"
215+
216+
217+
DBInterface.execute(conn, "DROP TABLE if exists blob_field")
218+
DBInterface.execute(conn, "CREATE TABLE blob_field (id int(11), t blob)")
219+
stmt = DBInterface.prepare(conn, "INSERT INTO blob_field (id, t) VALUES (?, ?);")
220+
DBInterface.execute(stmt, [-1, "hey there sailor"])
221+
res = DBInterface.execute(conn, "select id, t from blob_field") |> columntable
222+
@test length(res) == 2
223+
@test res[2][1] == [0x68, 0x65, 0x79, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x73, 0x61, 0x69, 0x6c, 0x6f, 0x72]

0 commit comments

Comments
 (0)