1
|
|
function generate_namedtuple(::Type{NamedTuple{names,types}}, q) where {names,types}
|
2
|
1
|
if @generated
|
3
|
1
|
vals = Tuple(:(getvalue(q, $i, $(fieldtype(types, i)))) for i = 1:fieldcount(types))
|
4
|
1
|
return :(NamedTuple{names,types}(($(vals...),)))
|
5
|
|
else
|
6
|
|
return NamedTuple{names,types}(Tuple(getvalue(q, i, fieldtype(types, i)) for i = 1:fieldcount(types)))
|
7
|
|
end
|
8
|
|
end
|
9
|
|
|
10
|
|
# This section of the code hijacks SQLite internals to make queryverse-compatible iterators
|
11
|
|
# TODO: submit as a PR to SQLite
|
12
|
|
struct SQLiteCursor{Row}
|
13
|
1
|
statement::Stmt
|
14
|
|
status::RefValue{Cint}
|
15
|
|
cursor_row::RefValue{Int}
|
16
|
|
end
|
17
|
|
|
18
|
|
function eltype(::SQLiteCursor{Row}) where {Row}
|
19
|
0
|
Row
|
20
|
|
end
|
21
|
|
function IteratorSize(::Type{<:SQLiteCursor})
|
22
|
0
|
SizeUnknown()
|
23
|
|
end
|
24
|
|
|
25
|
|
function isdone(cursor::SQLiteCursor)
|
26
|
1
|
status = cursor.status[]
|
27
|
1
|
if status == SQLITE_DONE
|
28
|
1
|
true
|
29
|
1
|
elseif status == SQLITE_ROW
|
30
|
1
|
false
|
31
|
0
|
elseif sqliteerror(cursor.statement.db)
|
32
|
0
|
false
|
33
|
|
else
|
34
|
0
|
error("Unknown SQLite cursor status")
|
35
|
|
end
|
36
|
|
end
|
37
|
|
|
38
|
|
function getvalue(cursor::SQLiteCursor, column_number::Int, ::Type{Value}) where {Value}
|
39
|
1
|
handle = cursor.statement.handle
|
40
|
1
|
column_type = sqlite3_column_type(handle, column_number)
|
41
|
1
|
if column_type == SQLITE_NULL
|
42
|
1
|
Value()
|
43
|
|
else
|
44
|
1
|
julia_type = juliatype(column_type) # native SQLite Int, Float, and Text types
|
45
|
1
|
sqlitevalue(
|
46
|
|
if julia_type === Any
|
47
|
|
if !isbitstype(Value)
|
48
|
0
|
Value
|
49
|
|
else
|
50
|
0
|
julia_type
|
51
|
|
end
|
52
|
|
else
|
53
|
1
|
julia_type
|
54
|
|
end, handle, column_number)
|
55
|
|
end
|
56
|
|
end
|
57
|
|
|
58
|
|
function iterate(cursor::SQLiteCursor{Row}) where {Row}
|
59
|
1
|
if isdone(cursor)
|
60
|
0
|
nothing
|
61
|
|
else
|
62
|
1
|
named_tuple = generate_namedtuple(Row, cursor)
|
63
|
1
|
cursor.cursor_row[] = 1
|
64
|
1
|
named_tuple, 1
|
65
|
|
end
|
66
|
|
end
|
67
|
|
|
68
|
|
function iterate(cursor::SQLiteCursor{Row}, state) where {Row}
|
69
|
1
|
if state != cursor.cursor_row[]
|
70
|
0
|
error("State does not match SQLiteCursor cursor_row")
|
71
|
|
else
|
72
|
1
|
cursor.status[] = sqlite3_step(cursor.statement.handle)
|
73
|
1
|
if isdone(cursor)
|
74
|
1
|
nothing
|
75
|
|
else
|
76
|
1
|
named_tuple = generate_namedtuple(Row, cursor)
|
77
|
1
|
cursor.cursor_row[] = state + 1
|
78
|
1
|
named_tuple, state + 1
|
79
|
|
end
|
80
|
|
end
|
81
|
|
end
|
82
|
|
|
83
|
|
function isiterable(::SourceCode)
|
84
|
0
|
true
|
85
|
|
end
|
86
|
|
function isiterabletable(::SourceCode)
|
87
|
0
|
true
|
88
|
|
end
|
89
|
|
function collect(source::SourceCode)
|
90
|
1
|
collect(getiterator(source))
|
91
|
|
end
|
92
|
|
|
93
|
|
function second((value_1, value_2))
|
94
|
0
|
value_2
|
95
|
|
end
|
96
|
|
|
97
|
|
function name_and_type(handle, column_number, nullable=true, strict_types=true)
|
98
|
0
|
Symbol(unsafe_string(sqlite3_column_name(handle, column_number))),
|
99
|
|
if strict_types
|
100
|
0
|
julia_type = juliatype(handle, column_number)
|
101
|
0
|
if nullable
|
102
|
0
|
DataValue{julia_type}
|
103
|
|
else
|
104
|
0
|
julia_type
|
105
|
|
end
|
106
|
|
else
|
107
|
0
|
Any
|
108
|
|
end
|
109
|
|
end
|
110
|
|
|
111
|
|
function getiterator(source_code::SourceCode)
|
112
|
1
|
statement = Stmt(
|
113
|
|
source_code.source,
|
114
|
|
string(finalize(translate(source_code.code)))
|
115
|
|
)
|
116
|
|
# bind!(statement, values)
|
117
|
1
|
status = execute(statement)
|
118
|
1
|
handle = statement.handle
|
119
|
|
schema = ntuple(
|
120
|
|
let handle = handle
|
121
|
1
|
column_number -> name_and_type(handle, column_number)
|
122
|
|
end,
|
123
|
|
sqlite3_column_count(handle)
|
124
|
|
)
|
125
|
1
|
SQLiteCursor{NamedTuple{Tuple(map(first, schema)),Tuple{map(second, schema)...}}}(statement, Ref(status), Ref(0))
|
126
|
|
end
|
127
|
|
|
128
|
|
# Use default show methods from the queryverse
|
129
|
|
function show(stream::IO, source::SourceCode)
|
130
|
1
|
printtable(stream, getiterator(source), "SQLite query result")
|
131
|
|
end
|
132
|
|
|
133
|
|
function showable(::MIME"text/html", source::SourceCode)
|
134
|
0
|
true
|
135
|
|
end
|
136
|
|
function show(stream::IO, ::MIME"text/html", source::SourceCode)
|
137
|
0
|
printHTMLtable(stream, getiterator(source))
|
138
|
|
end
|
139
|
|
|
140
|
|
function showable(::MIME"application/vnd.dataresource+json", source::SourceCode)
|
141
|
0
|
true
|
142
|
|
end
|
143
|
|
function show(stream::IO, ::MIME"application/vnd.dataresource+json", source::SourceCode)
|
144
|
0
|
printdataresource(stream, getiterator(source))
|
145
|
|
end
|