Skip to content

Commit 8468df0

Browse files
committed
Merge branch 'master' of git://github.com/quinnj/SQLite.jl
2 parents 520575b + d62a048 commit 8468df0

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ A Julia interface to the SQLite library and support for operations on DataFrames
5555

5656
Used to execute prepared `SQLiteStmt`. 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 `query` below. Also consider the `create`, `drop`, and `append` methods for manipulation statements as further SQLite performance tricks are incorporated automatically.
5757

58-
* `query(db::SQLiteDB, sql::String)`
58+
* `query(db::SQLiteDB, sql::String, values=[])`
5959

6060
An SQL statement `sql` is prepared, executed in the context of `db`, and results, if any, are returned. The return values are a `(String[],Any[])` tuple representing `(column names, result values)`.
6161

62+
The values in `values` are used in parameter binding (see `bind` above). If your statement uses nameless parameters `values` must be a Vector of the values you wish to bind to your statment. If your statement uses named parameters `values` must be a Dict where the keys are of type String or Symbol. The key must match an identifier name in the statement (the name **does not** include the ':', '@' or '$' prefix). You can not use a dictionary which has Strings *and* Symbols for it's keys.
63+
6264
* `create(db::SQLiteDB,name::String,table::AbstractMatrix,
6365
colnames=String[],coltypes=DataType[];temp::Bool=false)`
6466

src/SQLite.jl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@ function Base.close(stmt::SQLiteStmt)
9898
return
9999
end
100100

101+
# bind a row to nameless parameters
102+
function Base.bind(stmt::SQLiteStmt, values::Vector)
103+
nparams = sqlite3_bind_parameter_count(stmt.handle)
104+
@assert nparams == length(values) "you must provide values for all placeholders"
105+
for i in 1:nparams
106+
@inbounds bind(stmt, i, values[i])
107+
end
108+
end
109+
# bind a row to named parameters
110+
function Base.bind{V}(stmt::SQLiteStmt, values::Dict{Symbol, V})
111+
nparams = sqlite3_bind_parameter_count(stmt.handle)
112+
@assert nparams == length(values) "you must provide values for all placeholders"
113+
for i in 1:nparams
114+
name = bytestring(sqlite3_bind_parameter_name(stmt.handle, i))
115+
@assert !isempty(name) "nameless parameters should be passed as a tuple"
116+
# name is returned with the ':', '@' or '$' at the start
117+
name = name[2:end]
118+
bind(stmt, i, values[symbol(name)])
119+
end
120+
end
101121
# Binding parameters to SQL statements
102122
function Base.bind(stmt::SQLiteStmt,name::String,val)
103123
i = sqlite3_bind_parameter_index(stmt.handle,name)
@@ -141,8 +161,9 @@ end
141161

142162
sqldeserialize(r) = deserialize(IOBuffer(r))
143163

144-
function query(db::SQLiteDB,sql::String)
164+
function query(db::SQLiteDB,sql::String, values=[])
145165
stmt = SQLiteStmt(db,sql)
166+
bind(stmt, values)
146167
status = execute(stmt)
147168
ncols = sqlite3_column_count(stmt.handle)
148169
if status == SQLITE_DONE || ncols == 0

src/api.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ function sqlite3_finalize(stmt::Ptr{Void})
4949
Cint, (Ptr{Void},),
5050
stmt)
5151
end
52+
53+
# SQLITE_API int sqlite3_bind_paramter_count(sqlite3_stmt*)
54+
function sqlite3_bind_parameter_count(stmt::Ptr{Void})
55+
@NULLCHECK stmt
56+
return ccall( (:sqlite3_bind_parameter_count, sqlite3_lib),
57+
Cint, (Ptr{Void},),
58+
stmt)
59+
end
60+
#SQLITE_API const char* sqlite3_bind_parameter_name(sqlite3_stmt*, int)
61+
function sqlite3_bind_parameter_name(stmt::Ptr{Void}, col::Int)
62+
@NULLCHECK stmt
63+
return ccall( (:sqlite3_bind_parameter_name, sqlite3_lib),
64+
Ptr{Uint8}, (Ptr{Void}, Cint),
65+
stmt, col)
66+
end
5267
# SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
5368
function sqlite3_bind_parameter_index(stmt::Ptr{Void},value::String)
5469
@NULLCHECK stmt
@@ -108,8 +123,6 @@ end
108123
# SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
109124
# SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
110125

111-
# SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*);
112-
# SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
113126
# SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
114127

115128
function sqlite3_step(stmt::Ptr{Void})

test/runtests.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,29 @@ if VERSION > v"0.4.0-"
104104
@test drop(db,"temp") == EMPTY_RESULTSET
105105
end
106106

107+
query(db,"CREATE TABLE temp AS SELECT * FROM Album")
108+
r = query(db, "SELECT * FROM temp LIMIT ?", [3])
109+
@test size(r) == (3,3)
110+
r = query(db, "SELECT * FROM temp WHERE Title LIKE ?", ["%time%"])
111+
@test r.values[1] == [76, 111, 187]
112+
query(db, "INSERT INTO temp VALUES (?1, ?3, ?2)", [0,0,"Test Album"])
113+
r = query(db, "SELECT * FROM temp WHERE AlbumId = 0")
114+
@test r == ResultSet(Any["AlbumId", "Title", "ArtistId"], Any[Any[0], Any["Test Album"], Any[0]])
115+
drop(db, "temp")
116+
117+
# I can't be arsed to create a new one using old dictionary syntax
118+
if VERSION > v"0.4.0-"
119+
query(db,"CREATE TABLE temp AS SELECT * FROM Album")
120+
r = query(db, "SELECT * FROM temp LIMIT :a", Dict(:a => 3))
121+
@test size(r) == (3,3)
122+
r = query(db, "SELECT * FROM temp WHERE Title LIKE @word", Dict(:word => "%time%"))
123+
@test r.values[1] == [76, 111, 187]
124+
query(db, "INSERT INTO temp VALUES (@lid, :title, \$rid)", Dict(:rid => 0, :lid => 0, :title => "Test Album"))
125+
r = query(db, "SELECT * FROM temp WHERE AlbumId = 0")
126+
@test r == ResultSet(Any["AlbumId", "Title", "ArtistId"], Any[Any[0], Any["Test Album"], Any[0]])
127+
drop(db, "temp")
128+
end
129+
107130
r = query(db, sr"SELECT LastName FROM Employee WHERE BirthDate REGEXP '^\d{4}-08'")
108131
@test r.values[1][1] == "Peacock"
109132

0 commit comments

Comments
 (0)