Skip to content

Commit 270fe53

Browse files
authored
Allow passing value parameters to bind in SQLite.execute (#190)
* Allow passing value parameters to bind in SQLite.execute
1 parent 7be68ef commit 270fe53

File tree

3 files changed

+31
-27
lines changed

3 files changed

+31
-27
lines changed

docs/src/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ SQLite.load!
4949
> In the examples above, NNN is an integer value and AAA is an identifier. A parameter initially has a value of NULL. Prior to calling sqlite3_step() for the first time or immediately after sqlite3_reset(), the application can invoke one of the sqlite3_bind() interfaces to attach values to the parameters. Each call to sqlite3_bind() overrides prior bindings on the same parameter.
5050
5151

52-
* `SQLite.execute!(stmt::SQLite.Stmt)`
52+
* `SQLite.execute!(stmt::SQLite.Stmt; values=[])`
5353

5454
`SQLite.execute!(db::SQLite.DB, sql::String)`
5555

5656

57-
Used to execute a prepared `SQLite.Stmt`. The 2nd method is a convenience method to pass in an SQL statement as a string which gets prepared and executed in one call. This method does not check for or return any results, hence it is only useful for database manipulation methods (i.e. ALTER, CREATE, UPDATE, DROP). To return results, see `SQLite.query` below.
57+
Used to execute a prepared `SQLite.Stmt`. The 2nd method is a convenience method to pass in an SQL statement as a string which gets prepared and executed in one call. This method does not check for or return any results, hence it is only useful for database manipulation methods (i.e. ALTER, CREATE, UPDATE, DROP). To return results, see `SQLite.query` below. With a prepared `stmt`, you can also pass a `values` iterable or `Dict` that will bind to
58+
parameters in the prepared query.
5859

5960

6061
* `SQLite.Query(db::SQLite.DB, sql::String, values=[])`

src/SQLite.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,25 @@ Additional methods exist for working individual SQL parameters:
106106
"""
107107
function bind! end
108108

109+
bind!(stmt::Stmt, ::Nothing) = nothing
110+
109111
function bind!(stmt::Stmt, values::Tuple)
110112
nparams = sqlite3_bind_parameter_count(stmt.handle)
111-
@assert nparams == length(values) "you must provide values for all placeholders"
113+
@assert nparams == length(values) "you must provide values for all query placeholders"
112114
for i in 1:nparams
113115
@inbounds bind!(stmt, i, values[i])
114116
end
115117
end
116118
function bind!(stmt::Stmt, values::Vector)
117119
nparams = sqlite3_bind_parameter_count(stmt.handle)
118-
@assert nparams == length(values) "you must provide values for all placeholders"
120+
@assert nparams == length(values) "you must provide values for all query placeholders"
119121
for i in 1:nparams
120122
@inbounds bind!(stmt, i, values[i])
121123
end
122124
end
123125
function bind!(stmt::Stmt, values::Dict{Symbol, V}) where {V}
124126
nparams = sqlite3_bind_parameter_count(stmt.handle)
125-
@assert nparams == length(values) "you must provide values for all placeholders"
127+
@assert nparams == length(values) "you must provide values for all query placeholders"
126128
for i in 1:nparams
127129
name = unsafe_string(sqlite3_bind_parameter_name(stmt.handle, i))
128130
@assert !isempty(name) "nameless parameters should be passed as a Vector"
@@ -235,16 +237,16 @@ sqlitetype(::Type{Missing}) = "NULL"
235237
sqlitetype(x) = "BLOB"
236238

237239
"""
238-
`SQLite.execute!(stmt::SQLite.Stmt)` => `Cvoid`
239-
240-
`SQLite.execute!(db::DB, sql::String)` => `Cvoid`
241-
240+
`SQLite.execute!(stmt::SQLite.Stmt; values=[])` => `nothing`
241+
`SQLite.execute!(db::DB, sql::String)` => `nothing`
242242
243243
Execute a prepared SQLite statement, not checking for or returning any results.
244+
Will bind `values` to any parameters in `stmt`.
244245
"""
245246
function execute! end
246247

247-
function execute!(stmt::Stmt)
248+
function execute!(stmt::Stmt; values=nothing)
249+
bind!(stmt, values)
248250
r = sqlite3_step(stmt.handle)
249251
if r == SQLITE_DONE
250252
sqlite3_reset(stmt.handle)
@@ -256,8 +258,9 @@ function execute!(stmt::Stmt)
256258
return r
257259
end
258260

259-
function execute!(db::DB, sql::AbstractString)
261+
function execute!(db::DB, sql::AbstractString; values=nothing)
260262
stmt = Stmt(db, sql)
263+
bind!(stmt, values)
261264
r = execute!(stmt)
262265
finalize(stmt)
263266
return r

test/runtests.jl

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,24 @@ r = SQLite.Query(db, "select * from $tablename") |> DataFrame
9999
@test all([typeof(i) for i in r[1]] .== Dates.Date)
100100
SQLite.drop!(db, "$tablename")
101101

102-
SQLite.Query(db, "CREATE TABLE temp AS SELECT * FROM Album")
102+
SQLite.execute!(db, "CREATE TABLE temp AS SELECT * FROM Album")
103103
r = SQLite.Query(db, "SELECT * FROM temp LIMIT ?"; values=[3]) |> DataFrame
104104
@test size(r) == (3,3)
105105
r = SQLite.Query(db, "SELECT * FROM temp WHERE Title LIKE ?"; values=["%time%"]) |> DataFrame
106106
@test r[1] == [76, 111, 187]
107-
SQLite.Query(db, "INSERT INTO temp VALUES (?1, ?3, ?2)"; values=[0,0,"Test Album"])
107+
SQLite.execute!(db, "INSERT INTO temp VALUES (?1, ?3, ?2)"; values=[0,0,"Test Album"])
108108
r = SQLite.Query(db, "SELECT * FROM temp WHERE AlbumId = 0") |> DataFrame
109109
@test r[1][1] === 0
110110
@test r[2][1] == "Test Album"
111111
@test r[3][1] === 0
112112
SQLite.drop!(db, "temp")
113113

114-
SQLite.Query(db, "CREATE TABLE temp AS SELECT * FROM Album")
114+
SQLite.execute!(db, "CREATE TABLE temp AS SELECT * FROM Album")
115115
r = SQLite.Query(db, "SELECT * FROM temp LIMIT :a"; values=Dict(:a => 3)) |> DataFrame
116116
@test size(r) == (3,3)
117117
r = SQLite.Query(db, "SELECT * FROM temp WHERE Title LIKE @word"; values=Dict(:word => "%time%")) |> DataFrame
118118
@test r[1] == [76, 111, 187]
119-
SQLite.Query(db, "INSERT INTO temp VALUES (@lid, :title, \$rid)"; values=Dict(:rid => 0, :lid => 0, :title => "Test Album"))
119+
SQLite.execute!(db, "INSERT INTO temp VALUES (@lid, :title, \$rid)"; values=Dict(:rid => 0, :lid => 0, :title => "Test Album"))
120120
r = SQLite.Query(db, "SELECT * FROM temp WHERE AlbumId = 0") |> DataFrame
121121
@test r[1][1] === 0
122122
@test r[2][1] == "Test Album"
@@ -185,10 +185,10 @@ r = SQLite.Query(db, "SELECT bigsum(TrackId) FROM PlaylistTrack") |> DataFrame
185185
s = SQLite.Query(db, "SELECT TrackId FROM PlaylistTrack") |> DataFrame
186186
# @test r[1][1] == big(sum(convert(Vector{Int},s[1])))
187187

188-
SQLite.Query(db, "CREATE TABLE points (x INT, y INT, z INT)")
189-
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[1, 2, 3])
190-
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[4, 5, 6])
191-
SQLite.Query(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[7, 8, 9])
188+
SQLite.execute!(db, "CREATE TABLE points (x INT, y INT, z INT)")
189+
SQLite.execute!(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[1, 2, 3])
190+
SQLite.execute!(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[4, 5, 6])
191+
SQLite.execute!(db, "INSERT INTO points VALUES (?, ?, ?)"; values=[7, 8, 9])
192192

193193
sumpoint(p::Point3D, x, y, z) = p + Point3D(x, y, z)
194194
SQLite.register(db, Point3D(0, 0, 0), sumpoint)
@@ -197,7 +197,7 @@ r = SQLite.Query(db, "SELECT sumpoint(x, y, z) FROM points") |> DataFrame
197197
SQLite.drop!(db, "points")
198198

199199
db2 = SQLite.DB()
200-
SQLite.Query(db2, "CREATE TABLE tab1 (r REAL, s INT)")
200+
SQLite.execute!(db2, "CREATE TABLE tab1 (r REAL, s INT)")
201201

202202
@test_throws SQLite.SQLiteException SQLite.drop!(db2, "nonexistant")
203203
# should not throw anything
@@ -233,22 +233,22 @@ stmt = SQLite.Stmt(db, "INSERT INTO tbl (a) VALUES (@a);")
233233
SQLite.bind!(stmt, "@a", 1)
234234

235235
binddb = SQLite.DB()
236-
SQLite.Query(binddb, "CREATE TABLE temp (n NULL, i6 INT, f REAL, s TEXT, a BLOB)")
237-
SQLite.Query(binddb, "INSERT INTO temp VALUES (?1, ?2, ?3, ?4, ?5)"; values=Any[missing, convert(Int64,6), 6.4, "some text", b"bytearray"])
236+
SQLite.execute!(binddb, "CREATE TABLE temp (n NULL, i6 INT, f REAL, s TEXT, a BLOB)")
237+
SQLite.execute!(binddb, "INSERT INTO temp VALUES (?1, ?2, ?3, ?4, ?5)"; values=Any[missing, convert(Int64,6), 6.4, "some text", b"bytearray"])
238238
r = SQLite.Query(binddb, "SELECT * FROM temp") |> DataFrame
239239
@test isa(r[1][1], Missing)
240240
@test isa(r[2][1], Int)
241241
@test isa(r[3][1], Float64)
242242
@test isa(r[4][1], AbstractString)
243243
@test isa(r[5][1], Base.CodeUnits)
244-
SQLite.Query(binddb, "CREATE TABLE blobtest (a BLOB, b BLOB)")
245-
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", b"b"])
246-
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", BigInt(2)])
244+
SQLite.execute!(binddb, "CREATE TABLE blobtest (a BLOB, b BLOB)")
245+
SQLite.execute!(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", b"b"])
246+
SQLite.execute!(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", BigInt(2)])
247247

248248
p1 = Point(1, 2)
249249
p2 = Point(1.3, 2.4)
250-
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", p1])
251-
SQLite.Query(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", p2])
250+
SQLite.execute!(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", p1])
251+
SQLite.execute!(binddb, "INSERT INTO blobtest VALUES (?1, ?2)"; values=Any[b"a", p2])
252252
r = SQLite.Query(binddb, "SELECT * FROM blobtest"; stricttypes=false) |> DataFrame
253253
for value in r[1]
254254
@test value == b"a"

0 commit comments

Comments
 (0)