Skip to content

Commit 7a5698b

Browse files
committed
More doc updates for the new DataStreams implementation
1 parent a2a250a commit 7a5698b

File tree

7 files changed

+110
-80
lines changed

7 files changed

+110
-80
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ SQLite.jl
66
[![Build Status](https://travis-ci.org/JuliaDB/SQLite.jl.svg?branch=master)](https://travis-ci.org/JuliaDB/SQLite.jl)
77
[![Coverage Status](https://coveralls.io/repos/JuliaDB/SQLite.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/JuliaDB/SQLite.jl?branch=master)
88

9-
A Julia interface to the SQLite library and support for operations on DataFrames
9+
A Julia interface to the SQLite library and support for the `DataStreams` data processing framework.
1010

1111
**Installation**: `julia> Pkg.add("SQLite")`
1212

REQUIRE

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
julia 0.3
1+
julia 0.4
22
BinDeps
33
Compat
4+
DataStreams
5+
NullableArrays
6+
CSV
7+
Libz
48
@osx Homebrew
59
@windows WinRPM

src/SQLite.jl

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,7 @@ module SQLite
33
using Compat, NullableArrays, CSV, Libz, DataStreams
44
import CSV.PointerString
55

6-
#TODO
7-
# create old_ui.jl file w/ deprecations
8-
# make all function renames
9-
# make new types SQLite.DB/Stmt, get code from jq/updates
10-
# deprecate all old function names/types
11-
# create Source.jl, Sink.jl files
12-
# pull Source code from jq/updates
13-
# stream!(SQLite.Source,DataStream)
14-
# stream!(SQLite.Source,CSV.Sink)
15-
# stream!(DataStream,SQLite.Sink)
16-
# stream!(CSV.Source,SQLite.Sink)
17-
# rewrite tests
18-
# add new tests file for Source/Sink
19-
6+
# Deprecated exports
207
export NULL, SQLiteDB, SQLiteStmt, ResultSet,
218
execute, query, tables, indices, columns, droptable, dropindex,
229
create, createindex, append, deleteduplicates
@@ -35,7 +22,7 @@ immutable NullType end
3522
const NULL = NullType()
3623
show(io::IO,::NullType) = print(io,"#NULL")
3724

38-
# internal wrapper type to, in-effect, mark something which has been serialized
25+
"internal wrapper type to, in-effect, mark something which has been serialized"
3926
immutable Serialization
4027
object
4128
end
@@ -66,7 +53,9 @@ type DB
6653
end
6754
end
6855
end
56+
"`SQLite.DB(file::AbstractString)` opens or creates an SQLite database with `file`"
6957
DB(f::AbstractString) = DB(utf8(f))
58+
"`SQLite.DB()` creates an in-memory SQLite database"
7059
DB() = DB(":memory:")
7160

7261
function _close(db::DB)
@@ -77,6 +66,9 @@ end
7766

7867
Base.show(io::IO, db::SQLite.DB) = print(io, string("SQLite.DB(",db.file == ":memory:" ? "in-memory" : "\"$(db.file)\"",")"))
7968

69+
"""
70+
`SQLite.Stmt(db::DB, sql::AbstractString)` creates and prepares an SQLite statement
71+
"""
8072
type Stmt
8173
db::DB
8274
handle::Ptr{Void}
@@ -110,15 +102,15 @@ include("UDF.jl")
110102
include("old_ui.jl")
111103
export @sr_str, @register, register
112104

113-
# bind a row to nameless parameters
105+
"bind a row (`values`) to nameless parameters by index"
114106
function bind!(stmt::Stmt, values::Vector)
115107
nparams = sqlite3_bind_parameter_count(stmt.handle)
116108
@assert nparams == length(values) "you must provide values for all placeholders"
117109
for i in 1:nparams
118110
@inbounds bind!(stmt, i, values[i])
119111
end
120112
end
121-
# bind a row to named parameters
113+
"bind a row (`Dict(:key => value)`) to named parameters"
122114
function bind!{V}(stmt::Stmt, values::Dict{Symbol, V})
123115
nparams = sqlite3_bind_parameter_count(stmt.handle)
124116
@assert nparams == length(values) "you must provide values for all placeholders"
@@ -131,23 +123,24 @@ function bind!{V}(stmt::Stmt, values::Dict{Symbol, V})
131123
end
132124
end
133125
# Binding parameters to SQL statements
126+
"bind `val` to the named parameter `name`"
134127
function bind!(stmt::Stmt,name::AbstractString,val)
135128
i = sqlite3_bind_parameter_index(stmt.handle,name)
136129
if i == 0
137130
throw(SQLiteException("SQL parameter $name not found in $stmt"))
138131
end
139132
return bind!(stmt,i,val)
140133
end
141-
bind!(stmt::Stmt,i::Int,val::AbstractFloat) = sqlite3_bind_double(stmt.handle,i,Float64(val))
142-
bind!(stmt::Stmt,i::Int,val::Int32) = sqlite3_bind_int(stmt.handle,i,val)
143-
bind!(stmt::Stmt,i::Int,val::Int64) = sqlite3_bind_int64(stmt.handle,i,val)
144-
bind!(stmt::Stmt,i::Int,val::NullType) = sqlite3_bind_null(stmt.handle,i)
145-
bind!(stmt::Stmt,i::Int,val::AbstractString) = sqlite3_bind_text(stmt.handle,i,val)
146-
bind!(stmt::Stmt,i::Int,val::PointerString) = sqlite3_bind_text(stmt.handle,i,val.ptr,val.len)
147-
bind!(stmt::Stmt,i::Int,val::UTF16String) = sqlite3_bind_text16(stmt.handle,i,val)
134+
bind!(stmt::Stmt,i::Int,val::AbstractFloat) = (sqlite3_bind_double(stmt.handle,i,Float64(val)); return nothing)
135+
bind!(stmt::Stmt,i::Int,val::Int32) = (sqlite3_bind_int(stmt.handle,i,val); return nothing)
136+
bind!(stmt::Stmt,i::Int,val::Int64) = (sqlite3_bind_int64(stmt.handle,i,val); return nothing)
137+
bind!(stmt::Stmt,i::Int,val::NullType) = (sqlite3_bind_null(stmt.handle,i); return nothing)
138+
bind!(stmt::Stmt,i::Int,val::AbstractString) = (sqlite3_bind_text(stmt.handle,i,val); return nothing)
139+
bind!(stmt::Stmt,i::Int,val::PointerString) = (sqlite3_bind_text(stmt.handle,i,val.ptr,val.len); return nothing)
140+
bind!(stmt::Stmt,i::Int,val::UTF16String) = (sqlite3_bind_text16(stmt.handle,i,val); return nothing)
148141
# We may want to track the new ByteVec type proposed at https://github.com/JuliaLang/julia/pull/8964
149142
# as the "official" bytes type instead of Vector{UInt8}
150-
bind!(stmt::Stmt,i::Int,val::Vector{UInt8}) = sqlite3_bind_blob(stmt.handle,i,val)
143+
bind!(stmt::Stmt,i::Int,val::Vector{UInt8}) = (sqlite3_bind_blob(stmt.handle,i,val); return nothing)
151144
# Fallback is BLOB and defaults to serializing the julia value
152145
function sqlserialize(x)
153146
t = IOBuffer()
@@ -158,12 +151,13 @@ function sqlserialize(x)
158151
serialize(t,s)
159152
return takebuf_array(t)
160153
end
154+
"bind `val` to the parameter at index `i`"
161155
bind!(stmt::Stmt,i::Int,val) = bind!(stmt,i,sqlserialize(val))
162156
#TODO:
163157
#int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
164158
#int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
165159

166-
# Execute SQL statements
160+
"Execute a prepared SQLite statement"
167161
function execute!(stmt::Stmt)
168162
r = sqlite3_step(stmt.handle)
169163
if r == SQLITE_DONE
@@ -173,6 +167,7 @@ function execute!(stmt::Stmt)
173167
end
174168
return r
175169
end
170+
"Prepare and execute an SQLite statement"
176171
function execute!(db::DB,sql::AbstractString)
177172
stmt = Stmt(db,sql)
178173
return execute!(stmt)
@@ -192,28 +187,26 @@ function sqldeserialize(r)
192187
end
193188

194189
# Transaction-based commands
195-
function transaction(db, mode="DEFERRED")
196-
#=
197-
Begin a transaction in the spedified mode, default "DEFERRED".
190+
"""
191+
Begin a transaction in the spedified `mode`, default = "DEFERRED".
198192
199-
If mode is one of "", "DEFERRED", "IMMEDIATE" or "EXCLUSIVE" then a
200-
transaction of that (or the default) type is started. Otherwise a savepoint
201-
is created whose name is mode converted to AbstractString.
202-
=#
193+
If `mode` is one of "", "DEFERRED", "IMMEDIATE" or "EXCLUSIVE" then a
194+
transaction of that (or the default) type is started. Otherwise a savepoint
195+
is created whose name is `mode` converted to AbstractString.
196+
"""
197+
function transaction(db, mode="DEFERRED")
198+
execute!(db,"PRAGMA temp_store=MEMORY;")
203199
if uppercase(mode) in ["", "DEFERRED", "IMMEDIATE", "EXCLUSIVE"]
204200
execute!(db, "BEGIN $(mode) TRANSACTION;")
205201
else
206202
execute!(db, "SAVEPOINT $(mode);")
207203
end
208204
end
209-
205+
"Execute the function `f` within a transaction."
210206
function transaction(f::Function, db)
211-
#=
212-
Execute the function f within a transaction.
213-
=#
214207
# generate a random name for the savepoint
215208
name = string("SQLITE",randstring(10))
216-
execute!(db,"PRAGMA synchronous = OFF")
209+
execute!(db,"PRAGMA synchronous = OFF;")
217210
transaction(db, name)
218211
try
219212
f()
@@ -223,18 +216,21 @@ function transaction(f::Function, db)
223216
finally
224217
# savepoints are not released on rollback
225218
commit(db, name)
226-
execute!(db,"PRAGMA synchronous = ON")
219+
execute!(db,"PRAGMA synchronous = ON;")
227220
end
228221
end
229222

230-
# commit a transaction or savepoint (if name is given)
223+
"commit a transaction or named savepoint"
231224
commit(db) = execute!(db, "COMMIT TRANSACTION;")
225+
"commit a transaction or named savepoint"
232226
commit(db, name) = execute!(db, "RELEASE SAVEPOINT $(name);")
233227

234-
# rollback transaction or savepoint (if name is given)
228+
"rollback transaction or named savepoint"
235229
rollback(db) = execute!(db, "ROLLBACK TRANSACTION;")
230+
"rollback transaction or named savepoint"
236231
rollback(db, name) = execute!(db, "ROLLBACK TRANSACTION TO SAVEPOINT $(name);")
237232

233+
"drop the SQLite table `table` from the database `db`; `ifexists=true` will not return an error if `table` doesn't exist"
238234
function drop!(db::DB,table::AbstractString;ifexists::Bool=false)
239235
exists = ifexists ? "if exists" : ""
240236
transaction(db) do
@@ -243,15 +239,19 @@ function drop!(db::DB,table::AbstractString;ifexists::Bool=false)
243239
execute!(db,"vacuum")
244240
return
245241
end
246-
242+
"drop the SQLite index `index` from the database `db`; `ifexists=true` will not return an error if `index` doesn't exist"
247243
function dropindex!(db::DB,index::AbstractString;ifexists::Bool=false)
248244
exists = ifexists ? "if exists" : ""
249245
transaction(db) do
250246
execute!(db,"drop index $exists $index")
251247
end
252248
return
253249
end
254-
250+
"""
251+
create the SQLite index `index` on the table `table` using `cols`, which may be a single column or comma-delimited list of columns.
252+
`unique` specifies whether the index will be unique or not.
253+
`ifnotexists=true` will not throw an error if the index already exists
254+
"""
255255
function createindex!(db::DB,table::AbstractString,index::AbstractString,cols
256256
;unique::Bool=true,ifnotexists::Bool=false)
257257
u = unique ? "unique" : ""
@@ -262,8 +262,8 @@ function createindex!(db::DB,table::AbstractString,index::AbstractString,cols
262262
execute!(db,"analyze $index")
263263
return
264264
end
265-
266-
function deleteduplicates!(db,table::AbstractString,cols::AbstractString)
265+
"removes duplicate rows from `table` based on the values in `cols` which may be a single column or comma-delimited list of columns"
266+
function removeduplicates!(db,table::AbstractString,cols::AbstractString)
267267
transaction(db) do
268268
execute!(db,"delete from $table where rowid not in (select max(rowid) from $table group by $cols);")
269269
end
@@ -274,4 +274,4 @@ end
274274
include("Source.jl")
275275
include("Sink.jl")
276276

277-
end #SQLite module
277+
end # module

src/Sink.jl

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@ sqlitetype{T<:Integer}(::Type{T}) = "INT"
22
sqlitetype{T<:AbstractFloat}(::Type{T}) = "REAL"
33
sqlitetype{T<:AbstractString}(::Type{T}) = "TEXT"
44
sqlitetype(x) = "BLOB"
5-
5+
"SQLite.Sink implements the `Sink` interface in the `DataStreams` framework"
66
type Sink <: Data.Sink # <: IO
77
schema::Data.Schema
88
db::DB
99
tablename::UTF8String
1010
stmt::Stmt
1111
end
12-
13-
function Source(sink::SQLite.Sink)
14-
stmt = SQLite.Stmt(sink.db,"select * from $(sink.tablename)")
12+
"constructs an SQLite.Source from an SQLite.Sink; selects all rows/columns from the underlying Sink table by default"
13+
function Source(sink::SQLite.Sink,sql::AbstractString="select * from $(sink.tablename)")
14+
stmt = SQLite.Stmt(sink.db,sql)
1515
status = SQLite.execute!(stmt)
1616
return SQLite.Source(sink.schema, stmt, status)
1717
end
1818

19-
# independent Sink constructor for new or existing SQLite tables
19+
"""
20+
independent SQLite.Sink constructor to create a new or wrap an existing SQLite table with name `tablename`.
21+
can optionally provide a `Data.Schema` through the `schema` argument.
22+
`temp=true` will create a temporary SQLite table that will be destroyed automatically when the database is closed
23+
`ifnotexists=false` will throw an error if `tablename` already exists in `db`
24+
"""
2025
function Sink(db::DB,tablename::AbstractString="julia_"*randstring(),schema::Data.Schema=Data.EMPTYSCHEMA;temp::Bool=false,ifnotexists::Bool=true)
2126
rows, cols = size(schema)
2227
temp = temp ? "TEMP" : ""
@@ -27,59 +32,71 @@ function Sink(db::DB,tablename::AbstractString="julia_"*randstring(),schema::Dat
2732
stmt = SQLite.Stmt(db,"insert into $tablename values ($params)")
2833
return Sink(schema,db,utf8(tablename),stmt)
2934
end
35+
"constructs a new SQLite.Sink from the given `Data.Source`; uses `source` schema to create the SQLite table"
36+
function Sink(source::Data.Source, db::DB, tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=true)
37+
sink = Sink(db, tablename, source.schema; temp=temp, ifnotexists=ifnotexists)
38+
return Data.stream!(source,sink)
39+
end
3040

3141
# create a new SQLite table
3242
# Data.Table
3343
function getbind!{T}(dt::NullableVector{T},row,col,stmt)
34-
@inbounds SQLite.bind!(stmt,col,ifelse(dt.isnull[row], NULL, dt.values[row]::T))
44+
@inbounds val, isnull = dt.values[row]::T, dt.isnull[row]
45+
if isnull
46+
SQLite.bind!(stmt,col,NULL)
47+
else
48+
SQLite.bind!(stmt,col,val)
49+
end
3550
return
3651
end
37-
52+
"stream the data in `dt` into the SQLite table represented by `sink`"
3853
function Data.stream!(dt::Data.Table,sink::SQLite.Sink)
3954
rows, cols = size(dt)
4055
types = Data.types(dt)
56+
handle = sink.stmt.handle
4157
transaction(sink.db) do
4258
if rows*cols != 0
4359
for row = 1:rows
4460
for col = 1:cols
4561
@inbounds SQLite.getbind!(Data.column(dt,col,types[col]),row,col,sink.stmt)
4662
end
47-
SQLite.execute!(sink.stmt)
63+
SQLite.sqlite3_step(handle)
64+
SQLite.sqlite3_reset(handle)
4865
end
4966
end
5067
end
5168
SQLite.execute!(sink.db,"analyze $(sink.tablename)")
5269
return sink
5370
end
54-
function Sink(dt::Data.Table,db::DB,tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=false)
55-
sink = Sink(db,tablename,dt.schema;temp=temp,ifnotexists=ifnotexists)
56-
return Data.stream!(dt,sink)
57-
end
5871
# CSV.Source
5972
function getbind!{T}(io,::Type{T},opts,row,col,stmt)
6073
val, isnull = CSV.getfield(io,T,opts,row,col)
61-
SQLite.bind!(stmt,col,ifelse(isnull,NULL,val))
74+
if isnull
75+
SQLite.bind!(stmt,col,NULL)
76+
else
77+
SQLite.bind!(stmt,col,val)
78+
end
6279
return
6380
end
81+
"stream the data in `source` CSV file to the SQLite table represented by `sink`"
6482
function Data.stream!(source::CSV.Source,sink::SQLite.Sink)
6583
rows, cols = size(source)
6684
types = Data.types(source)
6785
io = source.data
6886
opts = source.options
87+
stmt = sink.stmt
88+
handle = stmt.handle
6989
transaction(sink.db) do
7090
if rows*cols != 0
7191
for row = 1:rows
7292
for col = 1:cols
73-
@inbounds SQLite.getbind!(io, types[col], opts, row, col, sink.stmt)
93+
@inbounds SQLite.getbind!(io, types[col], opts, row, col, stmt)
7494
end
75-
SQLite.execute!(sink.stmt)
95+
SQLite.sqlite3_step(handle)
96+
SQLite.sqlite3_reset(handle)
7697
end
7798
end
7899
end
79100
SQLite.execute!(sink.db,"analyze $(sink.tablename)")
80101
return sink
81102
end
82-
function Sink(csv::CSV.Source,db::DB,tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=false)
83-
sink = Sink(db,tablename,csv.schema;temp=temp,ifnotexists=ifnotexists)
84-
return Data.stream!(csv,sink)
85-
end

0 commit comments

Comments
 (0)