Skip to content

Commit 9123524

Browse files
committed
Project 'Full Nullability' update
1 parent 8c63889 commit 9123524

File tree

1 file changed

+42
-25
lines changed

1 file changed

+42
-25
lines changed

src/Source.jl

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@
44
Independently constructs an `SQLite.Source` in `db` with the SQL statement `sql`.
55
Will bind `values` to any parameters in `sql`.
66
`rows` is used to indicate how many rows to return in the query result if known beforehand. `rows=0` (the default) will return all possible rows.
7-
`stricttypes=false` will remove strict column typing in the result set, making each column effectively `Vector{Any}`
7+
`stricttypes=false` will remove strict column typing in the result set, making each column effectively `Vector{Any}`. `nullable::Bool=true` indicates
8+
whether to allow null values when fetching results; if set to `false` and a null value is encountered, a `NullException` will be thrown.
89
910
Note that no results are returned; `sql` is executed, and results are ready to be returned (i.e. streamed to an appropriate `Data.Sink` type)
1011
"""
11-
function Source(db::DB, sql::AbstractString, values=[]; rows::Int=-1, stricttypes::Bool=true)
12-
stmt = SQLite.Stmt(db,sql)
12+
function Source(db::DB, sql::AbstractString, values=[]; rows::Int=-1, stricttypes::Bool=true, nullable::Bool=true)
13+
stmt = SQLite.Stmt(db, sql)
1314
bind!(stmt, values)
1415
status = SQLite.execute!(stmt)
1516
cols = SQLite.sqlite3_column_count(stmt.handle)
16-
header = Array(String,cols)
17-
types = Array(DataType,cols)
17+
header = Array(String, cols)
18+
types = Array(DataType, cols)
1819
for i = 1:cols
19-
header[i] = unsafe_string(SQLite.sqlite3_column_name(stmt.handle,i))
20+
header[i] = unsafe_string(SQLite.sqlite3_column_name(stmt.handle, i))
2021
# do better column type inference; query what the column was created for?
21-
types[i] = stricttypes ? SQLite.juliatype(stmt.handle,i) : Any
22+
if nullable
23+
types[i] = Nullable{stricttypes ? SQLite.juliatype(stmt.handle, i) : Any}
24+
else
25+
types[i] = stricttypes ? SQLite.juliatype(stmt.handle, i) : Any
26+
end
2227
end
23-
return SQLite.Source(Data.Schema(header,types,rows),stmt,status)
28+
return SQLite.Source(Data.Schema(header, types, rows), stmt, status)
2429
end
2530

2631
"""
@@ -31,25 +36,25 @@ constructs an SQLite.Source from an SQLite.Sink; selects all rows/columns from t
3136
Source(sink::SQLite.Sink,sql::AbstractString="select * from $(sink.tablename)") = Source(sink.db, sql::AbstractString)
3237

3338
function juliatype(handle,col)
34-
x = SQLite.sqlite3_column_type(handle,col)
39+
x = SQLite.sqlite3_column_type(handle, col)
3540
if x == SQLITE_BLOB
36-
val = sqlitevalue(Any,handle,col)
41+
val = sqlitevalue(Any,handle, col)
3742
return typeof(val)
3843
else
3944
return juliatype(x)
4045
end
4146
end
4247
juliatype(x) = x == SQLITE_INTEGER ? Int : x == SQLITE_FLOAT ? Float64 : x == SQLITE_TEXT ? String : Any
4348

44-
sqlitevalue{T<:Union{Signed,Unsigned}}(::Type{T},handle,col) = convert(T, sqlite3_column_int64(handle,col))
45-
const FLOAT_TYPES = Union{Float16,Float32,Float64} # exclude BigFloat
46-
sqlitevalue{T<:FLOAT_TYPES}(::Type{T},handle,col) = convert(T, sqlite3_column_double(handle,col))
49+
sqlitevalue{T<:Union{Signed,Unsigned}}(::Type{T}, handle, col) = convert(T, sqlite3_column_int64(handle, col))
50+
const FLOAT_TYPES = Union{Float16, Float32, Float64} # exclude BigFloat
51+
sqlitevalue{T<:FLOAT_TYPES}(::Type{T}, handle, col) = convert(T, sqlite3_column_double(handle, col))
4752
#TODO: test returning a WeakRefString instead of calling `bytestring`
48-
sqlitevalue{T<:AbstractString}(::Type{T},handle,col) = convert(T,unsafe_string(sqlite3_column_text(handle,col)))
49-
function sqlitevalue{T}(::Type{T},handle,col)
50-
blob = convert(Ptr{UInt8},sqlite3_column_blob(handle,col))
51-
b = sqlite3_column_bytes(handle,col)
52-
buf = zeros(UInt8,b) # global const?
53+
sqlitevalue{T<:AbstractString}(::Type{T}, handle, col) = convert(T,unsafe_string(sqlite3_column_text(handle, col)))
54+
function sqlitevalue{T}(::Type{T}, handle, col)
55+
blob = convert(Ptr{UInt8}, sqlite3_column_blob(handle, col))
56+
b = sqlite3_column_bytes(handle, col)
57+
buf = zeros(UInt8, b) # global const?
5358
unsafe_copy!(pointer(buf), blob, b)
5459
r = sqldeserialize(buf)::T
5560
return r
@@ -67,14 +72,26 @@ Data.streamtype{T<:SQLite.Source}(::Type{T}, ::Type{Data.Field}) = true
6772
# `T` might be Int, Float64, String, WeakRefString, any Julia type, Any, NullType
6873
# `t` (the actual type of the value we're returning), might be SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, SQLITE_NULL
6974
# `SQLite.getfield` returns the next `Nullable{T}` value from the `SQLite.Source`
70-
function Data.getfield{T}(source::SQLite.Source, ::Type{T}, row, col)
75+
function Data.getfield{T}(source::SQLite.Source, ::Type{Nullable{T}}, row, col)
7176
handle = source.stmt.handle
72-
t = SQLite.sqlite3_column_type(handle,col)
77+
t = SQLite.sqlite3_column_type(handle, col)
7378
if t == SQLite.SQLITE_NULL
7479
val = Nullable{T}()
7580
else
7681
TT = SQLite.juliatype(t) # native SQLite Int, Float, and Text types
77-
val = Nullable{T}(sqlitevalue(ifelse(TT === Any && !isbits(T), T, TT),handle,col))
82+
val = Nullable{T}(sqlitevalue(ifelse(TT === Any && !isbits(T), T, TT), handle, col))
83+
end
84+
col == source.schema.cols && (source.status = sqlite3_step(handle))
85+
return val
86+
end
87+
function Data.getfield{T}(source::SQLite.Source, ::Type{T}, row, col)
88+
handle = source.stmt.handle
89+
t = SQLite.sqlite3_column_type(handle, col)
90+
if t == SQLite.SQLITE_NULL
91+
throw(NullException)
92+
else
93+
TT = SQLite.juliatype(t) # native SQLite Int, Float, and Text types
94+
val = sqlitevalue(ifelse(TT === Any && !isbits(T), T, TT), handle, col)
7895
end
7996
col == source.schema.cols && (source.status = sqlite3_step(handle))
8097
return val
@@ -89,13 +106,13 @@ Will bind `values` to any parameters in `sql`.
89106
`rows` is used to indicate how many rows to return in the query result if known beforehand. `rows=0` (the default) will return all possible rows.
90107
`stricttypes=false` will remove strict column typing in the result set, making each column effectively `Vector{Any}`
91108
"""
92-
function query(db::DB, sql::AbstractString, sink=DataFrame, args...; append::Bool=false, values=[], rows::Int=-1, stricttypes::Bool=true)
93-
source = Source(db, sql, values; rows=rows, stricttypes=stricttypes)
109+
function query(db::DB, sql::AbstractString, sink=DataFrame, args...; append::Bool=false, values=[], rows::Int=-1, stricttypes::Bool=true, nullable::Bool=true)
110+
source = Source(db, sql, values; rows=rows, stricttypes=stricttypes, nullable=nullable)
94111
return Data.stream!(source, sink, append, args...)
95112
end
96113

97-
function query{T}(db::DB, sql::AbstractString, sink::T; append::Bool=false, values=[], rows::Int=-1, stricttypes::Bool=true)
98-
source = Source(db, sql, values; rows=rows, stricttypes=stricttypes)
114+
function query{T}(db::DB, sql::AbstractString, sink::T; append::Bool=false, values=[], rows::Int=-1, stricttypes::Bool=true, nullable::Bool=true)
115+
source = Source(db, sql, values; rows=rows, stricttypes=stricttypes, nullable=nullable)
99116
return Data.stream!(source, sink, append)
100117
end
101118
query(source::SQLite.Source, sink=DataFrame, args...; append::Bool=false) = Data.stream!(source, sink, append, args...)

0 commit comments

Comments
 (0)