1
1
module SQLite
2
2
3
3
using Random, Serialization
4
- using WeakRefStrings, DataFrames
4
+ using WeakRefStrings, DBInterface
5
5
6
6
struct SQLiteException <: Exception
7
7
msg:: AbstractString
@@ -39,34 +39,36 @@ To create an in-memory temporary database, call `SQLite.DB()`.
39
39
The `SQLite.DB` will automatically closed/shutdown when it goes out of scope
40
40
(i.e. the end of the Julia session, end of a function call wherein it was created, etc.)
41
41
"""
42
- mutable struct DB
42
+ mutable struct DB <: DBInterface.Connection
43
43
file:: String
44
44
handle:: Ptr{Cvoid}
45
- changes:: Int
46
45
47
46
function DB (f:: AbstractString )
48
47
handle = Ref {Ptr{Cvoid}} ()
49
48
f = isempty (f) ? f : expanduser (f)
50
49
if @OK sqliteopen (f, handle)
51
- db = new (f, handle[], 0 )
50
+ db = new (f, handle[])
52
51
finalizer (_close, db)
53
52
return db
54
53
else # error
55
- db = new (f, handle[], 0 )
54
+ db = new (f, handle[])
56
55
finalizer (_close, db)
57
56
sqliteerror (db)
58
57
end
59
58
end
60
59
end
61
60
DB () = DB (" :memory:" )
61
+ DBInterface. connect (:: Type{DB} ) = DB ()
62
+ DBInterface. connect (:: Type{DB} , f:: AbstractString ) = DB (f)
63
+ DBInterface. close! (db:: DB ) = _close (db)
62
64
63
65
function _close (db:: DB )
64
66
db. handle == C_NULL || sqlite3_close_v2 (db. handle)
65
67
db. handle = C_NULL
66
68
return
67
69
end
68
70
69
- Base. show (io:: IO , db:: SQLite.DB ) = print (io, string (" SQLite.DB(" , db . file == " :memory: " ? " in-memory " : " \" $(db. file) \" " , " )" ))
71
+ Base. show (io:: IO , db:: SQLite.DB ) = print (io, string (" SQLite.DB(" , " \" $(db. file) \" " , " )" ))
70
72
71
73
"""
72
74
Constructs and prepares (compiled by the SQLite library)
@@ -75,25 +77,28 @@ Note the SQL statement is not actually executed,
75
77
but only compiled
76
78
(mainly for usage where the same statement
77
79
is repeated with different parameters bound as values.
78
- See [`SQLite.bind!`](@ref) below).
79
-
80
- The `SQLite.Stmt` will automatically closed/shutdown when it goes out of scope (i.e. the end of the Julia session, end of a function call wherein it was created, etc.)
81
80
81
+ The `SQLite.Stmt` will be automatically closed/shutdown when it goes out of scope
82
+ (i.e. the end of the Julia session, end of a function call wherein it was created, etc.),
83
+ but you can close `DBInterface.close!(stmt)` to explicitly and immediately close the statement.
82
84
"""
83
- mutable struct Stmt
85
+ mutable struct Stmt <: DBInterface.Statement
84
86
db:: DB
85
87
handle:: Ptr{Cvoid}
86
88
params:: Dict{Int, Any}
89
+ status:: Int
87
90
88
91
function Stmt (db:: DB ,sql:: AbstractString )
89
92
handle = Ref {Ptr{Cvoid}} ()
90
93
sqliteprepare (db, sql, handle, Ref {Ptr{Cvoid}} ())
91
- stmt = new (db, handle[], Dict {Int, Any} ())
94
+ stmt = new (db, handle[], Dict {Int, Any} (), 0 )
92
95
finalizer (_close, stmt)
93
96
return stmt
94
97
end
95
98
end
96
99
100
+ DBInterface. close! (stmt:: Stmt ) = _close (stmt)
101
+
97
102
function _close (stmt:: Stmt )
98
103
stmt. handle == C_NULL || sqlite3_finalize (stmt. handle)
99
104
stmt. handle = C_NULL
103
108
sqliteprepare (db, sql, stmt, null) = @CHECK db sqlite3_prepare_v2 (db. handle, sql, stmt, null)
104
109
105
110
include (" UDF.jl" )
106
- export @sr_str , @register , register
111
+ export @sr_str
107
112
108
113
"""
109
114
`SQLite.clear!(stmt::SQLite.Stmt)`
@@ -164,41 +169,36 @@ From the [SQLite documentation](https://www3.sqlite.org/cintro.html):
164
169
"""
165
170
function bind! end
166
171
167
- bind! (stmt:: Stmt , :: Nothing ) = nothing
168
-
169
- function bind! (stmt:: Stmt , values:: Tuple )
172
+ function bind! (stmt:: Stmt , params:: Union{NamedTuple, Dict} )
170
173
nparams = sqlite3_bind_parameter_count (stmt. handle)
171
- @assert nparams == length (values ) " you must provide values for all query placeholders"
174
+ @assert nparams == length (params ) " you must provide values for all query placeholders"
172
175
for i in 1 : nparams
173
- @inbounds bind! (stmt, i, values[i])
176
+ name = unsafe_string (sqlite3_bind_parameter_name (stmt. handle, i))
177
+ @assert ! isempty (name) " nameless parameters should be passed as a Vector"
178
+ # name is returned with the ':', '@' or '$' at the start
179
+ sym = Symbol (name[2 : end ])
180
+ haskey (params, sym) || throw (SQLiteException (" `$name ` not found in values keyword arguments to bind to sql statement" ))
181
+ bind! (stmt, i, params[sym])
174
182
end
175
183
end
176
- function bind! (stmt:: Stmt , values:: Vector )
184
+
185
+ function bind! (stmt:: Stmt , values:: Union{Vector, Tuple} )
177
186
nparams = sqlite3_bind_parameter_count (stmt. handle)
178
187
@assert nparams == length (values) " you must provide values for all query placeholders"
179
188
for i in 1 : nparams
180
189
@inbounds bind! (stmt, i, values[i])
181
190
end
182
191
end
183
- function bind! (stmt:: Stmt , values:: Dict{Symbol, V} ) where {V}
184
- nparams = sqlite3_bind_parameter_count (stmt. handle)
185
- for i in 1 : nparams
186
- name = unsafe_string (sqlite3_bind_parameter_name (stmt. handle, i))
187
- @assert ! isempty (name) " nameless parameters should be passed as a Vector"
188
- # name is returned with the ':', '@' or '$' at the start
189
- sym = Symbol (name[2 : end ])
190
- haskey (values, sym) || throw (SQLiteException (" `$name ` not found in values Dict to bind to sql statement" ))
191
- bind! (stmt, i, values[sym])
192
- end
193
- end
192
+
194
193
# Binding parameters to SQL statements
195
- function bind! (stmt:: Stmt ,name:: AbstractString , val)
194
+ function bind! (stmt:: Stmt , name:: AbstractString , val)
196
195
i:: Int = sqlite3_bind_parameter_index (stmt. handle, name)
197
196
if i == 0
198
197
throw (SQLiteException (" SQL parameter $name not found in $stmt " ))
199
198
end
200
199
return bind! (stmt, i, val)
201
200
end
201
+
202
202
bind! (stmt:: Stmt , i:: Int , val:: AbstractFloat ) = (stmt. params[i] = val; @CHECK stmt. db sqlite3_bind_double (stmt. handle, i ,Float64 (val)); return nothing )
203
203
bind! (stmt:: Stmt , i:: Int , val:: Int32 ) = (stmt. params[i] = val; @CHECK stmt. db sqlite3_bind_int (stmt. handle, i ,val); return nothing )
204
204
bind! (stmt:: Stmt , i:: Int , val:: Int64 ) = (stmt. params[i] = val; @CHECK stmt. db sqlite3_bind_int64 (stmt. handle, i ,val); return nothing )
@@ -295,24 +295,21 @@ sqlitetype(::Type{Missing}) = "NULL"
295
295
sqlitetype (x) = " BLOB"
296
296
297
297
"""
298
- Used to execute a prepared `SQLite.Stmt`.
299
- The 2nd method is a convenience method to pass in an SQL statement as a string
300
- which gets prepared and executed in one call.
301
- This method does not check for or return any results,
302
- hence it is only useful for database manipulation methods
303
- (i.e. ALTER, CREATE, UPDATE, DROP).
304
- To return results, see [`SQLite.Query`](@ref) above.
298
+ SQLite.execute(db::SQLite.DB, sql, [params])
299
+ SQLite.execute(stmt::SQLite.Stmt, [params])
305
300
306
- With a prepared `stmt`,
307
- you can also pass a `values` iterable or ` Dict`
308
- that will bind to parameters in the prepared query.
301
+ An internal method that takes a `db` and `sql` arguments, or an already prepared `stmt` (2nd method) ,
302
+ any positional parameters (`params` given as `Vector` or `Tuple`) or named parameters (`params` given as ` Dict` or `NamedTuple`),
303
+ binds any parameters, and executes the query.
309
304
305
+ The sqlite return status code is returned. To return results from a query, please see [`DBInterface.execute`](@ref).
310
306
"""
311
- function execute! end
307
+ function execute end
312
308
313
- function execute! (stmt:: Stmt ; values = nothing )
314
- bind! (stmt, values )
309
+ function execute (stmt:: Stmt , params = () )
310
+ bind! (stmt, params )
315
311
r = sqlite3_step (stmt. handle)
312
+ stmt. status = r
316
313
if r == SQLITE_DONE
317
314
sqlite3_reset (stmt. handle)
318
315
elseif r != SQLITE_ROW
@@ -323,13 +320,7 @@ function execute!(stmt::Stmt; values=nothing)
323
320
return r
324
321
end
325
322
326
- function execute! (db:: DB , sql:: AbstractString ; values= nothing )
327
- stmt = Stmt (db, sql)
328
- bind! (stmt, values)
329
- r = execute! (stmt)
330
- finalize (stmt)
331
- return r
332
- end
323
+ execute (db:: DB , sql:: AbstractString , params= ()) = execute (Stmt (db, sql), params)
333
324
334
325
"""
335
326
SQLite.esc_id(x::Union{AbstractString,Vector{AbstractString}})
@@ -397,18 +388,18 @@ In the second method, `func` is executed within a transaction (the transaction b
397
388
function transaction end
398
389
399
390
function transaction (db, mode= " DEFERRED" )
400
- execute! (db, " PRAGMA temp_store=MEMORY;" )
391
+ execute (db, " PRAGMA temp_store=MEMORY;" )
401
392
if uppercase (mode) in [" " , " DEFERRED" , " IMMEDIATE" , " EXCLUSIVE" ]
402
- execute! (db, " BEGIN $(mode) TRANSACTION;" )
393
+ execute (db, " BEGIN $(mode) TRANSACTION;" )
403
394
else
404
- execute! (db, " SAVEPOINT $(mode) ;" )
395
+ execute (db, " SAVEPOINT $(mode) ;" )
405
396
end
406
397
end
407
398
408
399
@inline function transaction (f:: Function , db)
409
400
# generate a random name for the savepoint
410
401
name = string (" SQLITE" , Random. randstring (10 ))
411
- execute! (db, " PRAGMA synchronous = OFF;" )
402
+ execute (db, " PRAGMA synchronous = OFF;" )
412
403
transaction (db, name)
413
404
try
414
405
f ()
418
409
finally
419
410
# savepoints are not released on rollback
420
411
commit (db, name)
421
- execute! (db, " PRAGMA synchronous = ON;" )
412
+ execute (db, " PRAGMA synchronous = ON;" )
422
413
end
423
414
end
424
415
@@ -432,8 +423,8 @@ commit a transaction or named savepoint
432
423
"""
433
424
function commit end
434
425
435
- commit (db) = execute! (db, " COMMIT TRANSACTION;" )
436
- commit (db, name) = execute! (db, " RELEASE SAVEPOINT $(name) ;" )
426
+ commit (db) = execute (db, " COMMIT TRANSACTION;" )
427
+ commit (db, name) = execute (db, " RELEASE SAVEPOINT $(name) ;" )
437
428
438
429
"""
439
430
`SQLite.rollback(db)`
@@ -445,8 +436,8 @@ rollback transaction or named savepoint
445
436
"""
446
437
function rollback end
447
438
448
- rollback (db) = execute! (db, " ROLLBACK TRANSACTION;" )
449
- rollback (db, name) = execute! (db, " ROLLBACK TRANSACTION TO SAVEPOINT $(name) ;" )
439
+ rollback (db) = execute (db, " ROLLBACK TRANSACTION;" )
440
+ rollback (db, name) = execute (db, " ROLLBACK TRANSACTION TO SAVEPOINT $(name) ;" )
450
441
451
442
"""
452
443
`SQLite.drop!(db, table; ifexists::Bool=true)`
@@ -456,9 +447,9 @@ drop the SQLite table `table` from the database `db`; `ifexists=true` will preve
456
447
function drop! (db:: DB , table:: AbstractString ; ifexists:: Bool = false )
457
448
exists = ifexists ? " IF EXISTS" : " "
458
449
transaction (db) do
459
- execute! (db, " DROP TABLE $exists $(esc_id (table)) " )
450
+ execute (db, " DROP TABLE $exists $(esc_id (table)) " )
460
451
end
461
- execute! (db, " VACUUM" )
452
+ execute (db, " VACUUM" )
462
453
return
463
454
end
464
455
@@ -470,7 +461,7 @@ drop the SQLite index `index` from the database `db`; `ifexists=true` will not r
470
461
function dropindex! (db:: DB , index:: AbstractString ; ifexists:: Bool = false )
471
462
exists = ifexists ? " IF EXISTS" : " "
472
463
transaction (db) do
473
- execute! (db, " DROP INDEX $exists $(esc_id (index)) " )
464
+ execute (db, " DROP INDEX $exists $(esc_id (index)) " )
474
465
end
475
466
return
476
467
end
@@ -486,9 +477,9 @@ function createindex!(db::DB, table::AbstractString, index::AbstractString, cols
486
477
u = unique ? " UNIQUE" : " "
487
478
exists = ifnotexists ? " IF NOT EXISTS" : " "
488
479
transaction (db) do
489
- execute! (db, " CREATE $u INDEX $exists $(esc_id (index)) ON $(esc_id (table)) ($(esc_id (cols)) )" )
480
+ execute (db, " CREATE $u INDEX $exists $(esc_id (index)) ON $(esc_id (table)) ($(esc_id (cols)) )" )
490
481
end
491
- execute! (db, " ANALYZE $index " )
482
+ execute (db, " ANALYZE $index " )
492
483
return
493
484
end
494
485
@@ -506,34 +497,34 @@ function removeduplicates!(db, table::AbstractString, cols::AbstractArray{T}) wh
506
497
end
507
498
colsstr = chop (colsstr)
508
499
transaction (db) do
509
- execute! (db, " DELETE FROM $(esc_id (table)) WHERE _ROWID_ NOT IN (SELECT max(_ROWID_) from $(esc_id (table)) GROUP BY $(colsstr) );" )
500
+ execute (db, " DELETE FROM $(esc_id (table)) WHERE _ROWID_ NOT IN (SELECT max(_ROWID_) from $(esc_id (table)) GROUP BY $(colsstr) );" )
510
501
end
511
- execute! (db, " ANALYZE $table " )
502
+ execute (db, " ANALYZE $table " )
512
503
return
513
504
end
514
505
515
506
include (" tables.jl" )
516
507
517
508
"""
518
- `SQLite.tables(db, sink=DataFrame )`
509
+ `SQLite.tables(db, sink=columntable )`
519
510
520
511
returns a list of tables in `db`
521
512
"""
522
- tables (db:: DB , sink= DataFrame ) = Query (db, " SELECT name FROM sqlite_master WHERE type='table';" ) |> sink
513
+ tables (db:: DB , sink= columntable ) = DBInterface . execute (db, " SELECT name FROM sqlite_master WHERE type='table';" ) |> sink
523
514
524
515
"""
525
- `SQLite.indices(db, sink=DataFrame )`
516
+ `SQLite.indices(db, sink=columntable )`
526
517
527
518
returns a list of indices in `db`
528
519
"""
529
- indices (db:: DB , sink= DataFrame ) = Query (db, " SELECT name FROM sqlite_master WHERE type='index';" ) |> sink
520
+ indices (db:: DB , sink= columntable ) = DBInterface . execute (db, " SELECT name FROM sqlite_master WHERE type='index';" ) |> sink
530
521
531
522
"""
532
- `SQLite.columns(db, table, sink=DataFrame )`
523
+ `SQLite.columns(db, table, sink=columntable )`
533
524
534
525
returns a list of columns in `table`
535
526
"""
536
- columns (db:: DB , table:: AbstractString , sink= DataFrame ) = Query (db, " PRAGMA table_info($(esc_id (table)) )" ) |> sink
527
+ columns (db:: DB , table:: AbstractString , sink= columntable ) = DBInterface . execute (db, " PRAGMA table_info($(esc_id (table)) )" ) |> sink
537
528
538
529
"""
539
530
`SQLite.last_insert_rowid(db)`
0 commit comments