Skip to content

Commit 4f3fab2

Browse files
committed
Avoid generating full namedtuples when fetching results, which has a heavy compilation cost. Fixes #182
1 parent eb24e4c commit 4f3fab2

File tree

1 file changed

+32
-20
lines changed

1 file changed

+32
-20
lines changed

src/tables.jl

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,27 @@ using Tables
22

33
sym(ptr) = ccall(:jl_symbol, Ref{Symbol}, (Ptr{UInt8},), ptr)
44

5-
struct Query{NT}
5+
struct Query
66
stmt::Stmt
77
status::Base.RefValue{Cint}
8+
names::Vector{Symbol}
9+
types::Vector{Type}
10+
lookup::Dict{Symbol, Int}
811
end
912

10-
Tables.istable(::Type{<:Query}) = true
11-
Tables.rowaccess(::Type{<:Query}) = true
13+
struct Row
14+
q::Query
15+
end
16+
17+
getquery(r::Row) = getfield(r, :q)
18+
19+
Tables.istable(::Type{Query}) = true
20+
Tables.rowaccess(::Type{Query}) = true
1221
Tables.rows(q::Query) = q
13-
Tables.schema(q::Query{NamedTuple{names, types}}) where {names, types} = Tables.Schema(names, types)
22+
Tables.schema(q::Query) = Tables.Schema(q.names, q.types)
1423

15-
Base.IteratorSize(::Type{<:Query}) = Base.SizeUnknown()
16-
Base.eltype(q::Query{NT}) where {NT} = NT
24+
Base.IteratorSize(::Type{Query}) = Base.SizeUnknown()
25+
Base.eltype(q::Query) = Row
1726

1827
function reset!(q::Query)
1928
sqlite3_reset(q.stmt.handle)
@@ -39,26 +48,29 @@ function getvalue(q::Query, col::Int, ::Type{T}) where {T}
3948
end
4049
end
4150

42-
function generate_namedtuple(::Type{NamedTuple{names, types}}, q) where {names, types}
43-
if @generated
44-
vals = Tuple(:(getvalue(q, $i, $(fieldtype(types, i)))) for i = 1:fieldcount(types))
45-
return :(NamedTuple{names, types}(($(vals...),)))
46-
else
47-
return NamedTuple{names, types}(Tuple(getvalue(q, i, fieldtype(types, i)) for i = 1:fieldcount(types)))
48-
end
51+
Base.getindex(r::Row, col::Int) = getvalue(getquery(r), col, getquery(r).types[col])
52+
53+
function Base.getindex(r::Row, col::Symbol)
54+
q = getquery(r)
55+
i = q.lookup[col]
56+
return getvalue(q, i, q.types[i])
57+
end
58+
59+
function Base.getproperty(r::Row, col::Symbol)
60+
q = getquery(r)
61+
i = q.lookup[col]
62+
return getvalue(q, i, q.types[i])
4963
end
5064

51-
function Base.iterate(q::Query{NT}) where {NT}
65+
function Base.iterate(q::Query)
5266
done(q) && return nothing
53-
nt = generate_namedtuple(NT, q)
54-
return nt, nothing
67+
return Row(q), nothing
5568
end
5669

57-
function Base.iterate(q::Query{NT}, ::Nothing) where {NT}
70+
function Base.iterate(q::Query, ::Nothing)
5871
q.status[] = sqlite3_step(q.stmt.handle)
5972
done(q) && return nothing
60-
nt = generate_namedtuple(NT, q)
61-
return nt, nothing
73+
return Row(q), nothing
6274
end
6375

6476
"""
@@ -93,7 +105,7 @@ function Query(db::DB, sql::AbstractString; values=[], stricttypes::Bool=true, n
93105
types[i] = stricttypes ? juliatype(stmt.handle, i) : Any
94106
end
95107
end
96-
return Query{NamedTuple{Tuple(header), Tuple{types...}}}(stmt, Ref(status))
108+
return Query(stmt, Ref(status), header, types, Dict(x=>i for (i, x) in enumerate(header)))
97109
end
98110

99111
# as a sink

0 commit comments

Comments
 (0)