Skip to content

Commit 6fd2045

Browse files
committed
cleaning, show for SQLExpression
1 parent a4c1fdf commit 6fd2045

12 files changed

Lines changed: 601 additions & 536 deletions

src/QueryOperators.jl

Lines changed: 0 additions & 130 deletions
This file was deleted.

src/QuerySQLite.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@ import TableTraits: isiterabletable
2222
export Database
2323

2424
include("utilities.jl")
25-
include("source.jl")
25+
include("database.jl")
26+
include("code_instead.jl")
2627
include("iterate.jl")
28+
include("model_row.jl")
2729
include("translate.jl")
28-
include("library.jl")
29-
include("QueryOperators.jl")
30-
include("realize.jl")
30+
include("show.jl")
31+
32+
# To add support for a new function
33+
# 1) Use `@code_instead` to specify the argument types
34+
# 2) If the function modifies the model row, define model_row_call(::typeof(function), ...)
35+
# 3a) If the function maps directly onto an SQL function, use @translate_default function SQL_function_symbol
36+
# 3b) Otherwise, define translate_call(::typeof(function), ...)
37+
# 4) If the SQL function uses special syntax, special case the SQLExpression show method
3138

3239
end # module

src/code_instead.jl

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# All code is attached to its underlying database source
2+
struct SourceCode{Source}
3+
source::Source
4+
code::Expr
5+
end
6+
7+
# Every time two `SourceCode` objects are combined, check to see whether they both come from the same source
8+
function pop_sources!(sources, something)
9+
something
10+
end
11+
function pop_sources!(sources, source_code::SourceCode)
12+
push!(sources, source_code.source)
13+
source_code.code
14+
end
15+
16+
function combine_sources(a_function, source_codes...)
17+
sources = Set(Any[])
18+
codes = partial_map(pop_sources!, sources, source_codes)
19+
if length(sources) != 1
20+
error("Expected exactly one source; got ($(sources...))")
21+
else
22+
SourceCode(first(sources), Expr(:call, a_function, codes...))
23+
end
24+
end
25+
26+
# `@code_instead` hijacks call to create Julia expressions instead of evaluating functions
27+
function numbered_argument(number)
28+
Symbol(string("argument", number))
29+
end
30+
31+
function assert_type(argument, type)
32+
Expr(:(::), argument, type)
33+
end
34+
35+
# Splat Varargs
36+
function maybe_splat(argument, a_type)
37+
if @capture a_type Vararg{AType_}
38+
Expr(:(...), argument)
39+
else
40+
argument
41+
end
42+
end
43+
44+
function code_instead(location, a_function, types...)
45+
arguments = ntuple(numbered_argument, length(types))
46+
Expr(:function,
47+
Expr(:call, a_function, map_unrolled(assert_type, arguments, types)...),
48+
Expr(:block, location, Expr(:call,
49+
combine_sources,
50+
a_function,
51+
map_unrolled(maybe_splat, arguments, types)...
52+
))
53+
)
54+
end
55+
56+
macro code_instead(a_function, types...)
57+
code_instead(__source__, a_function, types...) |> esc
58+
end
59+
60+
# Currently, query doesn't do anything
61+
function QueryOperators.query(source_code::SourceCode)
62+
source_code
63+
end
64+
65+
@code_instead (==) SourceCode Any
66+
@code_instead (==) Any SourceCode
67+
@code_instead (==) SourceCode SourceCode
68+
69+
@code_instead (!=) SourceCode Any
70+
@code_instead (!=) Any SourceCode
71+
@code_instead (!=) SourceCode SourceCode
72+
73+
@code_instead (!) SourceCode
74+
75+
@code_instead (&) SourceCode Any
76+
@code_instead (&) Any SourceCode
77+
@code_instead (&) SourceCode SourceCode
78+
79+
@code_instead (|) SourceCode Any
80+
@code_instead (|) Any SourceCode
81+
@code_instead (|) SourceCode SourceCode
82+
83+
@code_instead coalesce SourceCode Vararg{Any}
84+
85+
@code_instead QueryOperators.drop SourceCode Integer
86+
87+
@code_instead QueryOperators.filter SourceCode Any Expr
88+
89+
@code_instead QueryOperators.groupby SourceCode Any Expr Any Expr
90+
91+
@code_instead if_else SourceCode Any Any
92+
@code_instead if_else Any SourceCode Any
93+
@code_instead if_else Any Any SourceCode
94+
@code_instead if_else Any SourceCode SourceCode
95+
@code_instead if_else SourceCode Any SourceCode
96+
@code_instead if_else SourceCode SourceCode Any
97+
@code_instead if_else SourceCode SourceCode SourceCode
98+
99+
@code_instead in SourceCode Any
100+
@code_instead in Any SourceCode
101+
@code_instead in SourceCode SourceCode
102+
103+
@code_instead isequal SourceCode Any
104+
@code_instead isequal Any SourceCode
105+
@code_instead isequal SourceCode SourceCode
106+
107+
@code_instead isless SourceCode Any
108+
@code_instead isless Any SourceCode
109+
@code_instead isless SourceCode SourceCode
110+
111+
@code_instead ismissing SourceCode
112+
113+
@code_instead QueryOperators.join SourceCode SourceCode Any Expr Any Expr Any Expr
114+
115+
@code_instead length SourceCode
116+
117+
@code_instead QueryOperators.map SourceCode Any Expr
118+
119+
@code_instead occursin AbstractString SourceCode
120+
@code_instead occursin Regex SourceCode
121+
122+
@code_instead QueryOperators.orderby SourceCode Any Expr
123+
124+
@code_instead QueryOperators.orderby_descending SourceCode Any Expr
125+
126+
@code_instead secondary SourceCode
127+
128+
@code_instead startswith SourceCode Any
129+
@code_instead startswith Any SourceCode
130+
@code_instead startswith SourceCode SourceCode
131+
132+
@code_instead QueryOperators.take SourceCode Any
133+
134+
@code_instead QueryOperators.thenby SourceCode Any Expr
135+
136+
@code_instead QueryOperators.thenby_descending SourceCode Any Expr
137+
138+
@code_instead QueryOperators.unique SourceCode Any Expr

src/database.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
get_table_names(source)::Tuple{Symbol}
3+
4+
Get the names of the tables in `source`
5+
"""
6+
function get_table_names(source::DB)
7+
as_symbols(tables(source).name)
8+
end
9+
export get_table_names
10+
11+
"""
12+
get_column_names(source, table_name)::Tuple{Symbol}
13+
14+
Get the names of the columns in `table_name` in `source`
15+
"""
16+
function get_column_names(source::DB, table_name)
17+
as_symbols(columns(source, String(table_name)).name)
18+
end
19+
export get_column_names
20+
21+
"""
22+
struct Database{Source}
23+
24+
`source` need only support [`get_table_names`](@ref) and [`get_column_names`](@ref).
25+
"""
26+
struct Database{Source}
27+
source::Source
28+
end
29+
30+
function Database(filename::AbstractString)
31+
if endswith(filename, "sqlite")
32+
Database(SQLite.DB(filename))
33+
else
34+
error("Unsupported database type for $filename")
35+
end
36+
end
37+
38+
get_source(source_tables::Database) = getfield(source_tables, :source)
39+
40+
# getproperty overloading allows direct access to each table in the database
41+
function getproperty(source_tables::Database, table_name::Symbol)
42+
SourceCode(get_source(source_tables),
43+
Expr(:call, getproperty, source_tables, table_name)
44+
)
45+
end

src/iterate.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# This section of the code hijacks SQLite internals to make queryverse-compatible iterators
2+
# TODO: submit as a PR to SQLite
13
struct SQLiteCursor{Row}
24
statement::Stmt
35
status::RefValue{Cint}
@@ -99,7 +101,10 @@ end
99101

100102
function getiterator(source_code::SourceCode)
101103
# TODO REVIEW
102-
statement = Stmt(source_code.source, text(source_code))
104+
statement = Stmt(
105+
source_code.source,
106+
string(finalize(translate(source_code.code)))
107+
)
103108
# bind!(statement, values)
104109
status = execute!(statement)
105110
handle = statement.handle
@@ -115,6 +120,7 @@ function getiterator(source_code::SourceCode)
115120
}}(statement, Ref(status), Ref(0))
116121
end
117122

123+
# Use default show methods from the queryverse
118124
function show(stream::IO, source::SourceCode)
119125
printtable(stream, getiterator(source), "SQLite query result")
120126
end

0 commit comments

Comments
 (0)