Skip to content

Commit 63cee13

Browse files
committed
Drop DataFrame dep, add TableTraits integration
1 parent fcfb487 commit 63cee13

2 files changed

Lines changed: 75 additions & 17 deletions

File tree

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ QueryableBackend = "0898d9ac-042b-5d31-8dcc-959fb3365f19"
99
QueryOperators = "2aef5ad7-51ca-5a8f-8e88-e75cf067b44b"
1010
SQLite = "0aa819cd-b072-5ff4-a722-6bc24af294d9"
1111
TableTraits = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
12+
DataValues = "e7dc6d0d-1eca-5fa6-8ad6-5aecde8b7ea5"
13+
TableShowUtils = "5e66a065-1f0a-5976-b372-e0b8c017ca10"
1214

1315
[extras]
1416
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

src/QuerySQLite.jl

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import Base: !, &, |, ==, !=, coalesce, getproperty, in, isequal, isless, ismiss
44
using Base: Generator, NamedTuple, tail
55
import Base.Iterators: drop, take
66
using Base.Meta: quot
7-
import DataFrames: DataFrame
87
import MacroTools
98
using MacroTools: @capture
109
import QueryOperators
1110
import QueryOperators: orderby, query
1211
import SQLite
1312
using SQLite: columns, DB, tables
13+
import IteratorInterfaceExtensions, TableTraits
14+
using DataValues
15+
import TableShowUtils
1416

1517
map_unrolled(call, variables::Tuple{}) = ()
1618
map_unrolled(call, variables) =
@@ -56,14 +58,6 @@ get_column_names(outside::DB, table_name) =
5658
as_symbols(columns(outside, String(table_name)).name)
5759
export get_column_names
5860

59-
"""
60-
submit_to(outside, text)
61-
62-
Send `text` to `outside`
63-
"""
64-
submit_to(outside::DB, text) = DataFrame(Query(outside, text))
65-
export submit_to
66-
6761
"""
6862
abstract type OutsideTables{Outside} end
6963
@@ -408,14 +402,11 @@ column_or_columns(outside_code::OutsideCode) = (translate(outside_code.code),)
408402

409403
# SQLite interface
410404

411-
using DataFrames: DataFrame
412-
413405
to_symbols(them) = map_unrolled(Symbol, (them...,))
414406

415407
get_table_names(database::DB) = to_symbols(tables(database).name)
416408
get_column_names(database::DB, table_name) =
417409
to_symbols(SQLite.columns(database, String(table_name)).name)
418-
submit_to(database::DB, text) = DataFrame(SQLite.Query(database, text))
419410

420411
# dispatch
421412

@@ -444,10 +435,75 @@ translate(code::Expr) =
444435
# collect
445436
query(outside_code::OutsideCode) = outside_code
446437

447-
DataFrame(outside_code::OutsideCode) =
448-
submit_to(
449-
outside_code.outside,
450-
translate(outside_code.code)
451-
)
438+
struct SQLiteCursor{T}
439+
stmt::SQLite.Stmt
440+
status::Base.RefValue{Cint}
441+
cur_row::Base.RefValue{Int}
442+
end
443+
444+
Base.eltype(q::SQLiteCursor{T}) where {T} = T
445+
Base.IteratorSize(::Type{<:SQLiteCursor}) = Base.SizeUnknown()
446+
447+
function isdone(q::SQLiteCursor)
448+
st = q.status[]
449+
st == SQLite.SQLITE_DONE && return true
450+
st == SQLite.SQLITE_ROW || SQLite.sqliteerror(q.stmt.db)
451+
return false
452+
end
453+
454+
function SQLite.getvalue(q::SQLiteCursor, col::Int, ::Type{T}) where {T}
455+
handle = q.stmt.handle
456+
t = SQLite.sqlite3_column_type(handle, col)
457+
if t == SQLite.SQLITE_NULL
458+
return T()
459+
else
460+
TT = SQLite.juliatype(t) # native SQLite Int, Float, and Text types
461+
return SQLite.sqlitevalue(ifelse(TT === Any && !isbitstype(T), T, TT), handle, col)
462+
end
463+
end
464+
465+
466+
467+
function Base.iterate(q::SQLiteCursor{NT}) where {NT}
468+
isdone(q) && return nothing
469+
nt = SQLite.generate_namedtuple(NT, q)
470+
q.cur_row[] = 1
471+
return nt, 1
472+
end
473+
474+
function Base.iterate(q::SQLiteCursor{NT}, state) where {NT}
475+
state != q.cur_row[] && error("FOO")
476+
q.status[] = SQLite.sqlite3_step(q.stmt.handle)
477+
isdone(q) && return nothing
478+
nt = SQLite.generate_namedtuple(NT, q)
479+
q.cur_row[] = state + 1
480+
return nt, state + 1
481+
end
482+
483+
IteratorInterfaceExtensions.isiterable(::OutsideCode) = true
484+
TableTraits.isiterabletable(::OutsideCode) = true
485+
486+
function IteratorInterfaceExtensions.getiterator(outside_code::OutsideCode)
487+
# TODO REVIEW
488+
stricttypes = true
489+
nullable = true
490+
491+
stmt = SQLite.Stmt(outside_code.outside, translate(outside_code.code))
492+
# bind!(stmt, values)
493+
status = SQLite.execute!(stmt)
494+
cols = SQLite.sqlite3_column_count(stmt.handle)
495+
header = Vector{Symbol}(undef, cols)
496+
types = Vector{Type}(undef, cols)
497+
for i = 1:cols
498+
header[i] = Symbol(unsafe_string(SQLite.sqlite3_column_name(stmt.handle, i)))
499+
if nullable
500+
types[i] = stricttypes ? DataValue{SQLite.juliatype(stmt.handle, i)} : Any
501+
else
502+
types[i] = stricttypes ? SQLite.juliatype(stmt.handle, i) : Any
503+
end
504+
end
505+
return SQLiteCursor{NamedTuple{Tuple(header), Tuple{types...}}}(stmt, Ref(status), Ref(0))
506+
end
507+
452508

453509
end # module

0 commit comments

Comments
 (0)