Skip to content

Commit adfc7d5

Browse files
committed
Updates to support DataFrames as default return datastructure
1 parent a03586d commit adfc7d5

File tree

7 files changed

+132
-400
lines changed

7 files changed

+132
-400
lines changed

src/SQLite.jl

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
using DataStreams
1+
VERSION >= v"0.4.0-dev+6521" && __precompile__(true)
22
module SQLite
33

4-
using Compat, NullableArrays, DataStreams, CSV
5-
const PointerString = Data.PointerString
6-
const NULLSTRING = Data.NULLSTRING
4+
using CSV, DataStreams, DataFrames, NullableArrays, WeakRefStrings
5+
6+
export Data
7+
8+
if !isdefined(Core, :String)
9+
typealias String UTF8String
10+
unsafe_string(ptr, len) = bytestring(ptr, len)
11+
end
12+
13+
if Base.VERSION < v"0.5.0-dev+4631"
14+
unsafe_wrap{A<:Array}(::Type{A}, ptr, len) = pointer_to_array(ptr, len)
15+
unsafe_string(ptr, len) = utf8(ptr, len)
16+
unsafe_wrap(::Type{String}, ptr, len) = unsafe_string(ptr, len)
17+
end
718

819
type SQLiteException <: Exception
920
msg::AbstractString
@@ -21,16 +32,16 @@ show(io::IO,::NullType) = print(io,"#NULL")
2132
# Normal constructor from filename
2233
sqliteopen(file,handle) = sqlite3_open(file,handle)
2334
sqliteopen(file::UTF16String,handle) = sqlite3_open16(file,handle)
24-
sqliteerror() = throw(SQLiteException(Compat.bytestring(sqlite3_errmsg())))
25-
sqliteerror(db) = throw(SQLiteException(Compat.bytestring(sqlite3_errmsg(db.handle))))
35+
sqliteerror() = throw(SQLiteException(unsafe_string(sqlite3_errmsg())))
36+
sqliteerror(db) = throw(SQLiteException(unsafe_string(sqlite3_errmsg(db.handle))))
2637

2738
"represents an SQLite database, either backed by an on-disk file or in-memory"
2839
type DB
29-
file::Compat.UTF8String
40+
file::String
3041
handle::Ptr{Void}
3142
changes::Int
3243

33-
function DB(f::Compat.UTF8String)
44+
function DB(f::AbstractString)
3445
handle = Ref{Ptr{Void}}()
3546
f = isempty(f) ? f : expanduser(f)
3647
if @OK sqliteopen(f,handle)
@@ -44,10 +55,8 @@ type DB
4455
end
4556
end
4657
end
47-
"`SQLite.DB(file::AbstractString)` opens or creates an SQLite database with `file`"
48-
DB(f::AbstractString) = DB(Compat.UTF8String(f))
4958
"`SQLite.DB()` creates an in-memory SQLite database"
50-
DB() = DB(Compat.UTF8String(":memory:"))
59+
DB() = DB(":memory:")
5160

5261
function _close(db::DB)
5362
sqlite3_close_v2(db.handle)
@@ -81,15 +90,7 @@ end
8190

8291
sqliteprepare(db,sql,stmt,null) = @CHECK db sqlite3_prepare_v2(db.handle,sql,stmt,null)
8392

84-
# TO DEPRECATE
85-
type SQLiteDB{T<:AbstractString}
86-
file::T
87-
handle::Ptr{Void}
88-
changes::Int
89-
end
90-
SQLiteDB(file,handle) = SQLiteDB(file,handle,0)
9193
include("UDF.jl")
92-
include("old_ui.jl")
9394
export @sr_str, @register, register
9495

9596
"bind a row (`values`) to nameless parameters by index"
@@ -105,11 +106,11 @@ function bind!{V}(stmt::Stmt, values::Dict{Symbol, V})
105106
nparams = sqlite3_bind_parameter_count(stmt.handle)
106107
@assert nparams == length(values) "you must provide values for all placeholders"
107108
for i in 1:nparams
108-
name = Compat.bytestring(sqlite3_bind_parameter_name(stmt.handle, i))
109+
name = unsafe_string(sqlite3_bind_parameter_name(stmt.handle, i))
109110
@assert !isempty(name) "nameless parameters should be passed as a Vector"
110111
# name is returned with the ':', '@' or '$' at the start
111112
name = name[2:end]
112-
bind!(stmt, i, values[@compat(Symbol)(name)])
113+
bind!(stmt, i, values[Symbol(name)])
113114
end
114115
end
115116
# Binding parameters to SQL statements
@@ -126,12 +127,12 @@ bind!(stmt::Stmt,i::Int,val::Int32) = (sqlite3_bind_int(stmt.handle,i,v
126127
bind!(stmt::Stmt,i::Int,val::Int64) = (sqlite3_bind_int64(stmt.handle,i,val); return nothing)
127128
bind!(stmt::Stmt,i::Int,val::NullType) = (sqlite3_bind_null(stmt.handle,i); return nothing)
128129
bind!(stmt::Stmt,i::Int,val::AbstractString) = (sqlite3_bind_text(stmt.handle,i,val); return nothing)
129-
bind!(stmt::Stmt,i::Int,val::PointerString{UInt8}) = (sqlite3_bind_text(stmt.handle,i,val.ptr,val.len); return nothing)
130-
bind!(stmt::Stmt,i::Int,val::PointerString{UInt16}) = (sqlite3_bind_text16(stmt.handle,i,val.ptr,val.len*2); return nothing)
130+
bind!(stmt::Stmt,i::Int,val::WeakRefString{UInt8}) = (sqlite3_bind_text(stmt.handle,i,val.ptr,val.len); return nothing)
131+
bind!(stmt::Stmt,i::Int,val::WeakRefString{UInt16}) = (sqlite3_bind_text16(stmt.handle,i,val.ptr,val.len*2); return nothing)
131132
bind!(stmt::Stmt,i::Int,val::UTF16String) = (sqlite3_bind_text16(stmt.handle,i,val); return nothing)
132-
function bind!(stmt::Stmt,i::Int,val::PointerString{UInt32})
133+
function bind!(stmt::Stmt,i::Int,val::WeakRefString{UInt32})
133134
A = UTF32String(pointer_to_array(val.ptr, val.len+1, false))
134-
return bind!(stmt, i, convert(Compat.UTF8String,A))
135+
return bind!(stmt, i, convert(String,A))
135136
end
136137
# We may want to track the new ByteVec type proposed at https://github.com/JuliaLang/julia/pull/8964
137138
# as the "official" bytes type instead of Vector{UInt8}
@@ -310,7 +311,7 @@ end
310311
type Sink <: Data.Sink # <: IO
311312
schema::Data.Schema
312313
db::DB
313-
tablename::Compat.UTF8String
314+
tablename::String
314315
stmt::Stmt
315316
end
316317

src/Sink.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ can optionally provide an existing SQLite table name or new name that a created
1111
`temp=true` will create a temporary SQLite table that will be destroyed automatically when the database is closed
1212
`ifnotexists=false` will throw an error if `tablename` already exists in `db`
1313
"""
14-
function Sink(schema::Data.Schema,db::DB,tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=true)
14+
function Sink(db::DB, schema::Data.Schema, tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=true)
1515
rows, cols = size(schema)
1616
temp = temp ? "TEMP" : ""
1717
ifnotexists = ifnotexists ? "IF NOT EXISTS" : ""
@@ -27,12 +27,12 @@ function Sink(source::SQLite.Source, tablename::AbstractString="julia_"*randstri
2727
return Sink(source.schema, source.db, tablename; temp=temp, ifnotexists=ifnotexists)
2828
end
2929
"constructs a new SQLite.Sink from the given `Data.Source`; uses `source` schema to create the SQLite table"
30-
function Sink(source::Data.Source, db::DB, tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=true)
31-
return Sink(source.schema, db, tablename; temp=temp, ifnotexists=ifnotexists)
30+
function Sink(db::DB, source, tablename::AbstractString="julia_"*randstring();temp::Bool=false,ifnotexists::Bool=true)
31+
return Sink(db, Data.schema(source), tablename; temp=temp, ifnotexists=ifnotexists)
3232
end
3333

3434
# create a new SQLite table
35-
# Data.Table
35+
# DataFrame
3636
function getbind!{T}(dt::NullableVector{T},row,col,stmt)
3737
@inbounds isnull = dt.isnull[row]
3838
if isnull
@@ -43,16 +43,21 @@ function getbind!{T}(dt::NullableVector{T},row,col,stmt)
4343
end
4444
return
4545
end
46+
function getbind!{T}(dt::Vector{T}, row, col, stmt)
47+
@inbounds val = dt[row]::T
48+
SQLite.bind!(stmt, col, val)
49+
return
50+
end
4651
"stream the data in `dt` into the SQLite table represented by `sink`"
47-
function Data.stream!(dt::Data.Table,sink::SQLite.Sink)
52+
function Data.stream!(dt::DataFrame, sink::SQLite.Sink)
4853
rows, cols = size(dt)
4954
types = Data.types(dt)
5055
handle = sink.stmt.handle
5156
transaction(sink.db) do
5257
if rows*cols != 0
5358
for row = 1:rows
5459
for col = 1:cols
55-
@inbounds SQLite.getbind!(Data.unsafe_column(dt,col,types[col]),row,col,sink.stmt)
60+
@inbounds SQLite.getbind!(dt.columns[col],row,col,sink.stmt)
5661
end
5762
SQLite.sqlite3_step(handle)
5863
SQLite.sqlite3_reset(handle)

src/Source.jl

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ function Source(db::DB, sql::AbstractString, values=[]; rows::Int=0, stricttypes
1919
bind!(stmt, values)
2020
status = SQLite.execute!(stmt)
2121
cols = SQLite.sqlite3_column_count(stmt.handle)
22-
header = Array(Compat.UTF8String,cols)
22+
header = Array(String,cols)
2323
types = Array(DataType,cols)
2424
for i = 1:cols
25-
header[i] = Compat.bytestring(SQLite.sqlite3_column_name(stmt.handle,i))
25+
header[i] = unsafe_string(SQLite.sqlite3_column_name(stmt.handle,i))
2626
# do better column type inference; query what the column was created for?
2727
types[i] = stricttypes ? SQLite.juliatype(stmt.handle,i) : Any
2828
end
@@ -41,23 +41,23 @@ function juliatype(handle,col)
4141
return juliatype(x)
4242
end
4343
end
44-
juliatype(x) = x == SQLITE_INTEGER ? Int : x == SQLITE_FLOAT ? Float64 : x == SQLITE_TEXT ? Compat.UTF8String : Any
44+
juliatype(x) = x == SQLITE_INTEGER ? Int : x == SQLITE_FLOAT ? Float64 : x == SQLITE_TEXT ? String : Any
4545

4646
sqlitevalue{T<:Union{Signed,Unsigned}}(::Type{T},handle,col) = convert(T, sqlite3_column_int64(handle,col))
4747
const FLOAT_TYPES = Union{Float16,Float32,Float64} # exclude BigFloat
4848
sqlitevalue{T<:FLOAT_TYPES}(::Type{T},handle,col) = convert(T, sqlite3_column_double(handle,col))
49-
#TODO: test returning a PointerString instead of calling `bytestring`
50-
sqlitevalue{T<:AbstractString}(::Type{T},handle,col) = convert(T,Compat.bytestring(sqlite3_column_text(handle,col)))
49+
#TODO: test returning a WeakRefString instead of calling `bytestring`
50+
sqlitevalue{T<:AbstractString}(::Type{T},handle,col) = convert(T,unsafe_string(sqlite3_column_text(handle,col)))
5151
function sqlitevalue{T}(::Type{T},handle,col)
52-
blob = convert(Ptr{UInt8},SQLite.sqlite3_column_blob(handle,col))
53-
b = SQLite.sqlite3_column_bytes(handle,col)
52+
blob = convert(Ptr{UInt8},sqlite3_column_blob(handle,col))
53+
b = sqlite3_column_bytes(handle,col)
5454
buf = zeros(UInt8,b) # global const?
5555
unsafe_copy!(pointer(buf), blob, b)
56-
r = SQLite.sqldeserialize(buf)::T
56+
r = sqldeserialize(buf)::T
5757
return r
5858
end
5959

60-
# `T` might be Int, Float64, String, PointerString, any Julia type, Any, NullType
60+
# `T` might be Int, Float64, String, WeakRefString, any Julia type, Any, NullType
6161
# `t` (the actual type of the value we're returning), might be SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, SQLITE_NULL
6262
"`SQLite.getfield` returns the next `Nullable{T}` value from the `SQLite.Source`"
6363
function getfield{T}(source::SQLite.Source, ::Type{T}, row, col)
@@ -73,40 +73,39 @@ function getfield{T}(source::SQLite.Source, ::Type{T}, row, col)
7373
return val
7474
end
7575

76-
function getfield!{T}(source::SQLite.Source, dest::NullableVector{T}, ::Type{T}, row, col)
76+
function getfield!{T}(source::SQLite.Source, dest::NullableVector{T}, row, col)
7777
@inbounds dest[row] = SQLite.getfield(source, T, row, col)
7878
return
7979
end
80-
function pushfield!{T}(source::SQLite.Source, dest::NullableVector{T}, ::Type{T}, row, col)
80+
function pushfield!{T}(source::SQLite.Source, dest::NullableVector{T}, row, col)
8181
push!(dest, SQLite.getfield(source, T, row, col))
8282
return
8383
end
84-
"streams data from the SQLite.Source to a Data.Table"
85-
function Data.stream!(source::SQLite.Source,sink::Data.Table)
84+
"streams data from the SQLite.Source to a DataFrame"
85+
function Data.stream!(source::SQLite.Source,sink::DataFrame)
8686
rows, cols = size(source)
8787
types = Data.types(source)
8888
if rows == 0
8989
row = 0
9090
while !Data.isdone(source)
9191
for col = 1:cols
9292
@inbounds T = types[col]
93-
SQLite.pushfield!(source, Data.unsafe_column(sink,col,T), T, row, col)
93+
SQLite.pushfield!(source, sink.columns[col], row, col)
9494
end
9595
row += 1
9696
end
9797
source.schema.rows = row
9898
else
9999
for row = 1:rows, col = 1:cols
100100
@inbounds T = types[col]
101-
SQLite.getfield!(source, Data.unsafe_column(sink,col,T), T, row, col)
101+
SQLite.getfield!(source, sink.columns[col], row, col)
102102
end
103103
end
104-
sink.schema = source.schema
105104
return sink
106105
end
107-
"creates a new Data.Table according to `source` schema and streams `Source` data into it"
108-
function Data.stream!(source::SQLite.Source,::Type{Data.Table})
109-
sink = Data.Table(source.schema)
106+
"creates a new DataFrame according to `source` schema and streams `Source` data into it"
107+
function Data.stream!(source::SQLite.Source,::Type{DataFrame})
108+
sink = DataFrame(source.schema)
110109
return Data.stream!(source,sink)
111110
end
112111
"streams data from an SQLite.Source to a CSV.Sink file; `header=false` will not write the column names to the file"
@@ -127,10 +126,10 @@ function Data.stream!(source::SQLite.Source,sink::CSV.Sink;header::Bool=true)
127126
close(sink)
128127
return sink
129128
end
130-
"convenience method for executing an SQL statement and streaming the results back in a Data.Table"
129+
"convenience method for executing an SQL statement and streaming the results back in a DataFrame"
131130
function query(db::DB,sql::AbstractString, values=[];rows::Int=0,stricttypes::Bool=true)
132131
so = Source(db,sql,values;rows=rows,stricttypes=stricttypes)
133-
return Data.stream!(so,Data.Table)
132+
return Data.stream!(so,DataFrame)
134133
end
135134

136135
"returns a list of tables in `db`"

src/UDF.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function sqlvalue(values, i)
1212
return sqlite3_value_double(temp_val_ptr)
1313
elseif valuetype == SQLITE_TEXT
1414
# TODO: have a way to return UTF16
15-
return Compat.bytestring(sqlite3_value_text(temp_val_ptr))
15+
return unsafe_string(sqlite3_value_text(temp_val_ptr))
1616
elseif valuetype == SQLITE_BLOB
1717
nbytes = sqlite3_value_bytes(temp_val_ptr)
1818
blob = sqlite3_value_blob(temp_val_ptr)
@@ -32,7 +32,7 @@ sqlreturn(context, val::UTF16String) = sqlite3_result_text16(context, val)
3232
sqlreturn(context, val::AbstractString) = sqlite3_result_text(context, val)
3333
sqlreturn(context, val::Vector{UInt8}) = sqlite3_result_blob(context, val)
3434

35-
sqlreturn(context, val::Bool) = sqlreturn(context, @compat Int(val))
35+
sqlreturn(context, val::Bool) = sqlreturn(context, Int(val))
3636
sqlreturn(context, val) = sqlreturn(context, sqlserialize(val))
3737

3838
# Internal method for generating an SQLite scalar function from
@@ -196,7 +196,7 @@ function register(db, func::Function; nargs::Int=-1, name::AbstractString=string
196196
nargs < -1 && (nargs = -1)
197197
@assert sizeof(name) <= 255 "size of function name must be <= 255"
198198

199-
f = eval(scalarfunc(func,@compat(Symbol)(name)))
199+
f = eval(scalarfunc(func,Symbol(name)))
200200

201201
cfunc = cfunction(f, Void, (Ptr{Void}, Cint, Ptr{Ptr{Void}}))
202202
# TODO: allow the other encodings

0 commit comments

Comments
 (0)