Skip to content

Commit fcbb090

Browse files
committed
Merge branch 'master' of git://github.com/quinnj/SQLite.jl
2 parents 7db8ef2 + ebc7ac7 commit fcbb090

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed

src/SQLite.jl

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ immutable NullType end
1616
const NULL = NullType()
1717
Base.show(io::IO,::NullType) = print(io,"NULL")
1818

19+
# internal wrapper type to, in-effect, mark something which has been serialized
20+
type Serialization
21+
object
22+
end
23+
1924
type ResultSet
2025
colnames
2126
values::Vector{Any}
@@ -132,13 +137,18 @@ Base.bind(stmt::SQLiteStmt,i::Int,val::Int64) = @CHECK stmt.db sqlite3_
132137
Base.bind(stmt::SQLiteStmt,i::Int,val::NullType) = @CHECK stmt.db sqlite3_bind_null(stmt.handle,i)
133138
Base.bind(stmt::SQLiteStmt,i::Int,val::AbstractString) = @CHECK stmt.db sqlite3_bind_text(stmt.handle,i,val)
134139
Base.bind(stmt::SQLiteStmt,i::Int,val::UTF16String) = @CHECK stmt.db sqlite3_bind_text16(stmt.handle,i,val)
140+
Base.bind(stmt::SQLiteStmt,i::Int,val::Vector{UInt8}) = @CHECK stmt.db sqlite3_bind_blob(stmt.handle,i,val)
135141
# Fallback is BLOB and defaults to serializing the julia value
136142
function sqlserialize(x)
137143
t = IOBuffer()
138-
serialize(t,x)
144+
# deserialize will sometimes return a random object when called on an array
145+
# which has not been previously serialized, we can use this type to check
146+
# that the array has been serialized
147+
s = Serialization(x)
148+
serialize(t,s)
139149
return takebuf_array(t)
140150
end
141-
Base.bind(stmt::SQLiteStmt,i::Int,val) = @CHECK stmt.db sqlite3_bind_blob(stmt.handle,i,sqlserialize(val))
151+
Base.bind(stmt::SQLiteStmt,i::Int,val) = bind(stmt,i,sqlserialize(val))
142152
#TODO:
143153
#int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
144154
#int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
@@ -159,7 +169,23 @@ function execute(db::SQLiteDB,sql::AbstractString)
159169
return changes(db)
160170
end
161171

162-
sqldeserialize(r) = deserialize(IOBuffer(r))
172+
function sqldeserialize(r)
173+
# try blocks introduce new scope
174+
local v
175+
# deserialize will sometimes, but not consistently (see comment in
176+
# sqlserialize), throw an error when called on an object which hasn't been
177+
# previously serialized
178+
try
179+
v = deserialize(IOBuffer(r))
180+
catch
181+
return r
182+
end
183+
if isa(v, Serialization)
184+
return v.object
185+
else
186+
return r
187+
end
188+
end
163189

164190
function query(db::SQLiteDB,sql::AbstractString, values=[])
165191
stmt = SQLiteStmt(db,sql)

src/UDF.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ sqlreturn(context, val::Int64) = sqlite3_result_int64(context, val)
3030
sqlreturn(context, val::Float64) = sqlite3_result_double(context, val)
3131
sqlreturn(context, val::UTF16String) = sqlite3_result_text16(context, val)
3232
sqlreturn(context, val::AbstractString) = sqlite3_result_text(context, val)
33-
sqlreturn(context, val) = sqlite3_result_blob(context, sqlserialize(val))
33+
sqlreturn(context, val::Vector{UInt8}) = sqlite3_result_blob(context, val)
3434

3535
sqlreturn(context, val::Bool) = sqlreturn(context, int(val))
36+
sqlreturn(context, val) = sqlreturn(context, sqlserialize(val))
3637

3738
# Internal method for generating an SQLite scalar function from
3839
# a Julia function name

test/runtests.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,34 @@ r = query(db, "SELECT * FROM temp WHERE AlbumId = 0")
114114
@test r == ResultSet(Any["AlbumId", "Title", "ArtistId"], Any[Any[0], Any["Test Album"], Any[0]])
115115
drop(db, "temp")
116116

117+
binddb = SQLiteDB()
118+
query(binddb, "CREATE TABLE temp (n NULL, i6 INT, f REAL, s TEXT, a BLOB)")
119+
query(binddb, "INSERT INTO temp VALUES (?1, ?2, ?3, ?4, ?5)", Any[NULL, int64(6), 6.4, "some text", b"bytearray"])
120+
r = query(binddb, "SELECT * FROM temp")
121+
for (v, t) in zip(r.values, [SQLite.NullType, Int64, Float64, AbstractString, Vector{UInt8}])
122+
@test isa(v[1], t)
123+
end
124+
query(binddb, "CREATE TABLE blobtest (a BLOB, b BLOB)")
125+
query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)", Any[b"a", b"b"])
126+
query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)", Any[b"a", BigInt(2)])
127+
type Point{T}
128+
x::T
129+
y::T
130+
end
131+
==(a::Point, b::Point) = a.x == b.x && a.y == b.y
132+
p1 = Point(1, 2)
133+
p2 = Point(1.3, 2.4)
134+
query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)", Any[b"a", p1])
135+
query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)", Any[b"a", p2])
136+
r = query(binddb, "SELECT * FROM blobtest")
137+
for v in r.values[1]
138+
@test v == b"a"
139+
end
140+
for (v1, v2) in zip(r.values[2], Any[b"b", BigInt(2), p1, p2])
141+
@test v1 == v2
142+
end
143+
close(binddb)
144+
117145
# I can't be arsed to create a new one using old dictionary syntax
118146
if VERSION > v"0.4.0-"
119147
query(db,"CREATE TABLE temp AS SELECT * FROM Album")
@@ -161,6 +189,14 @@ SQLite.register(db, hypot; nargs=2, name="hypotenuse")
161189
v = query(db, "select hypotenuse(Milliseconds,bytes) from track limit 5")
162190
@test [int(i) for i in v[1]] == [11175621,5521062,3997652,4339106,6301714]
163191

192+
SQLite.@register db str2arr(s) = convert(Array{UInt8}, s)
193+
r = query(db, "SELECT str2arr(LastName) FROM Employee LIMIT 2")
194+
@test r[1] == Any[UInt8[0x41,0x64,0x61,0x6d,0x73],UInt8[0x45,0x64,0x77,0x61,0x72,0x64,0x73]]
195+
196+
SQLite.@register db big
197+
r = query(db, "SELECT big(5)")
198+
@test r[1][1] == big(5)
199+
164200
@test size(tables(db)) == (11,1)
165201

166202
close(db)

0 commit comments

Comments
 (0)