Skip to content

Commit 44c9f40

Browse files
committed
Fix #164 by making sure we check the return code from binding values and also make sure we reset a statement when it errors
1 parent 2623c68 commit 44c9f40

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

src/SQLite.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ include("api.jl")
1313
# Normal constructor from filename
1414
sqliteopen(file, handle) = sqlite3_open(file, handle)
1515
sqliteerror(db) = throw(SQLiteException(unsafe_string(sqlite3_errmsg(db.handle))))
16+
sqliteexception(db) = SQLiteException(unsafe_string(sqlite3_errmsg(db.handle)))
1617

1718
"""
1819
represents an SQLite database, either backed by an on-disk file or in-memory
@@ -138,14 +139,14 @@ function bind!(stmt::Stmt,name::AbstractString, val)
138139
end
139140
return bind!(stmt, i, val)
140141
end
141-
bind!(stmt::Stmt, i::Int, val::AbstractFloat) = (stmt.params[i] = val; sqlite3_bind_double(stmt.handle, i ,Float64(val)); return nothing)
142-
bind!(stmt::Stmt, i::Int, val::Int32) = (stmt.params[i] = val; sqlite3_bind_int(stmt.handle, i ,val); return nothing)
143-
bind!(stmt::Stmt, i::Int, val::Int64) = (stmt.params[i] = val; sqlite3_bind_int64(stmt.handle, i ,val); return nothing)
144-
bind!(stmt::Stmt, i::Int, val::Missing) = (stmt.params[i] = val; sqlite3_bind_null(stmt.handle, i ); return nothing)
145-
bind!(stmt::Stmt, i::Int, val::AbstractString) = (stmt.params[i] = val; sqlite3_bind_text(stmt.handle, i ,val); return nothing)
146-
bind!(stmt::Stmt, i::Int, val::WeakRefString{UInt8}) = (stmt.params[i] = val; sqlite3_bind_text(stmt.handle, i, val.ptr, val.len); return nothing)
147-
bind!(stmt::Stmt, i::Int, val::WeakRefString{UInt16}) = (stmt.params[i] = val; sqlite3_bind_text16(stmt.handle, i, val.ptr, val.len*2); return nothing)
148-
bind!(stmt::Stmt, i::Int, val::Vector{UInt8}) = (stmt.params[i] = val; sqlite3_bind_blob(stmt.handle, i, val); return nothing)
142+
bind!(stmt::Stmt, i::Int, val::AbstractFloat) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_double(stmt.handle, i ,Float64(val)); return nothing)
143+
bind!(stmt::Stmt, i::Int, val::Int32) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_int(stmt.handle, i ,val); return nothing)
144+
bind!(stmt::Stmt, i::Int, val::Int64) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_int64(stmt.handle, i ,val); return nothing)
145+
bind!(stmt::Stmt, i::Int, val::Missing) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_null(stmt.handle, i ); return nothing)
146+
bind!(stmt::Stmt, i::Int, val::AbstractString) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text(stmt.handle, i ,val); return nothing)
147+
bind!(stmt::Stmt, i::Int, val::WeakRefString{UInt8}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text(stmt.handle, i, val.ptr, val.len); return nothing)
148+
bind!(stmt::Stmt, i::Int, val::WeakRefString{UInt16}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_text16(stmt.handle, i, val.ptr, val.len*2); return nothing)
149+
bind!(stmt::Stmt, i::Int, val::Vector{UInt8}) = (stmt.params[i] = val; @CHECK stmt.db sqlite3_bind_blob(stmt.handle, i, val); return nothing)
149150
# Fallback is BLOB and defaults to serializing the julia value
150151

151152
# internal wrapper mutable struct to, in-effect, mark something which has been serialized
@@ -248,7 +249,9 @@ function execute!(stmt::Stmt)
248249
if r == SQLITE_DONE
249250
sqlite3_reset(stmt.handle)
250251
elseif r != SQLITE_ROW
251-
sqliteerror(stmt.db)
252+
e = sqliteexception(stmt.db)
253+
sqlite3_reset(stmt.handle)
254+
throw(e)
252255
end
253256
return r
254257
end

test/runtests.jl

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,29 @@ using SQLite
22
using Test, Dates, Random, WeakRefStrings, Tables, DataFrames
33

44
import Base: +, ==
5+
mutable struct Point{T}
6+
x::T
7+
y::T
8+
end
9+
==(a::Point, b::Point) = a.x == b.x && a.y == b.y
10+
11+
mutable struct Point3D{T<:Number}
12+
x::T
13+
y::T
14+
z::T
15+
end
16+
==(a::Point3D, b::Point3D) = a.x == b.x && a.y == b.y && a.z == b.z
17+
+(a::Point3D, b::Point3D) = Point3D(a.x + b.x, a.y + b.y, a.z + b.z)
18+
519

620
dbfile = joinpath(dirname(pathof(SQLite)),"../test/Chinook_Sqlite.sqlite")
721
dbfile2 = joinpath(tempdir(), "test.sqlite")
822
cp(dbfile, dbfile2; force=true)
923
chmod(dbfile2, 0o777)
1024
db = SQLite.DB(dbfile2)
1125

26+
@testset "SQLite" begin
27+
1228
# regular SQLite tests
1329
ds = SQLite.Query(db, "SELECT name FROM sqlite_master WHERE type='table';") |> columntable
1430
@test length(ds) == 1
@@ -175,13 +191,7 @@ SQLite.Query(db, "CREATE TABLE points (x INT, y INT, z INT)")
175191
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[1, 2, 3])
176192
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[4, 5, 6])
177193
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[7, 8, 9])
178-
mutable struct Point3D{T<:Number}
179-
x::T
180-
y::T
181-
z::T
182-
end
183-
==(a::Point3D, b::Point3D) = a.x == b.x && a.y == b.y && a.z == b.z
184-
+(a::Point3D, b::Point3D) = Point3D(a.x + b.x, a.y + b.y, a.z + b.z)
194+
185195
sumpoint(p::Point3D, x, y, z) = p + Point3D(x, y, z)
186196
SQLite.register(db, Point3D(0, 0, 0), sumpoint)
187197
r = SQLite.Query(db, "SELECT sumpoint(x, y, z) FROM points") |> DataFrame
@@ -236,11 +246,7 @@ r = SQLite.Query(binddb, "SELECT * FROM temp") |> DataFrame
236246
SQLite.Query(binddb, "CREATE TABLE blobtest (a BLOB, b BLOB)")
237247
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", b"b"])
238248
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", BigInt(2)])
239-
mutable struct Point{T}
240-
x::T
241-
y::T
242-
end
243-
==(a::Point, b::Point) = a.x == b.x && a.y == b.y
249+
244250
p1 = Point(1, 2)
245251
p2 = Point(1.3, 2.4)
246252
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", p1])
@@ -276,3 +282,15 @@ GC.gc() # this MUST NOT garbage collect any of the bound values
276282
SQLite.clear!(stmt)
277283
GC.gc() # this will garbage collect the no longer bound values
278284
@test isempty(wkdict)
285+
286+
db = SQLite.DB()
287+
SQLite.execute!(db, "CREATE TABLE T (a TEXT, PRIMARY KEY (a))")
288+
289+
q = SQLite.Stmt(db, "INSERT INTO T VALUES(?)")
290+
SQLite.bind!(q, 1, "a")
291+
SQLite.execute!(q)
292+
293+
SQLite.bind!(q, 1, "a")
294+
@test_throws SQLite.SQLiteException SQLite.execute!(q)
295+
296+
end #@testset

0 commit comments

Comments
 (0)