@@ -8,11 +8,14 @@ defmodule SQL do
8
8
|> Enum . fetch! ( 1 )
9
9
@ moduledoc since: "0.1.0"
10
10
11
+ @ adapters [ SQL.Adapters.ANSI , SQL.Adapters.MySQL , SQL.Adapters.Postgres , SQL.Adapters.TDS ]
12
+
11
13
defmacro __using__ ( opts ) do
12
14
quote bind_quoted: [ opts: opts ] do
13
15
@ doc false
14
16
@ behaviour SQL
15
17
import SQL
18
+ @ sql_adapter opts [ :adapter ]
16
19
def sql_config , do: unquote ( opts )
17
20
def token_to_sql ( token ) , do: token_to_sql ( token )
18
21
defoverridable token_to_sql: 1
@@ -27,7 +30,15 @@ defmodule SQL do
27
30
@ doc deprecated: "Use SQL.Token.token_to_string/1 instead"
28
31
@ callback token_to_sql ( token :: { atom , keyword , list } ) :: String . t ( )
29
32
30
- defstruct [ :tokens , :params , :module , :id ]
33
+ defstruct [ :tokens , :params , :module , :id , :string , :inspect ]
34
+
35
+ defimpl Inspect , for: SQL do
36
+ def inspect ( sql , _opts ) , do: Inspect.Algebra . concat ( [ "~SQL\" \" \" \n " , sql . inspect , "\n \" \" \" " ] )
37
+ end
38
+
39
+ defimpl String.Chars , for: SQL do
40
+ def to_string ( sql ) , do: sql . string
41
+ end
31
42
32
43
@ doc """
33
44
Returns a parameterized SQL.
@@ -38,7 +49,7 @@ defmodule SQL do
38
49
{"select id, email from users where email = ?", ["[email protected] "]}
39
50
"""
40
51
@ doc since: "0.1.0"
41
- def to_sql ( % { params: params , id: id , module: module } ) , do: { :persistent_term . get ( { module , id , :plan } ) , params }
52
+ def to_sql ( sql ) , do: { sql . string , sql . params }
42
53
43
54
@ doc """
44
55
Handles the sigil `~SQL` for SQL.
@@ -59,11 +70,11 @@ defmodule SQL do
59
70
@ doc false
60
71
@ doc since: "0.1.0"
61
72
def parse ( binary ) do
62
- { :ok , _opts , _ , _ , _ , _ , tokens } = SQL.Lexer . lex ( binary , { 1 , 0 , nil } , 0 , [ format: true ] )
73
+ { :ok , _opts , _ , _ , _ , _ , tokens } = SQL.Lexer . lex ( binary , __ENV__ . file , 0 , [ format: true ] )
63
74
tokens
64
75
|> SQL.Parser . parse ( )
65
76
|> to_query ( )
66
- |> to_string ( SQL.String )
77
+ |> to_string ( SQL.Adapters.ANSI )
67
78
end
68
79
69
80
@ doc false
@@ -85,16 +96,19 @@ defmodule SQL do
85
96
token
86
97
end
87
98
88
- defimpl Inspect , for: SQL do
89
- def inspect ( sql , _opts ) , do: Inspect.Algebra . concat ( [ "~SQL\" \" \" \n " , :persistent_term . get ( { sql . id , :inspect } ) , "\n \" \" \" " ] )
90
- end
91
-
92
- defimpl String.Chars , for: SQL do
93
- def to_string ( % { id: id , module: module } ) , do: :persistent_term . get ( { module , id , :plan } )
94
- def to_string ( % { tokens: tokens , module: module } ) , do: SQL . to_string ( tokens , module )
95
- end
96
-
97
99
@ doc false
100
+ def to_string ( tokens , module ) when module in @ adapters do
101
+ tokens
102
+ |> Enum . reduce ( [ ] , fn
103
+ token , [ ] = acc -> [ acc | module . token_to_string ( token ) ]
104
+ token , acc ->
105
+ case module . token_to_string ( token ) do
106
+ << ";" , _ :: binary >> = v -> [ acc | v ]
107
+ v -> [ acc , " " | v ]
108
+ end
109
+ end )
110
+ |> IO . iodata_to_binary ( )
111
+ end
98
112
def to_string ( tokens , module ) do
99
113
fun = cond do
100
114
Kernel . function_exported? ( module , :sql_config , 0 ) -> & module . sql_config ( ) [ :adapter ] . token_to_string ( & 1 )
@@ -115,34 +129,46 @@ defmodule SQL do
115
129
116
130
@ doc false
117
131
def build ( left , { :<<>> , _ , _ } = right , _modifiers , env ) do
118
- data = build ( left , right )
119
- quote bind_quoted: [ module: env . module , left: Macro . unpipe ( left ) , right: right , file: env . file , id: id ( data ) , data: data ] do
120
- plan_inspect ( data , id )
121
- { t , p } = Enum . reduce ( left , { [ ] , [ ] } , fn
122
- { [ ] , 0 } , acc -> acc
123
- { v , 0 } , { t , p } ->
124
- { t ++ v . tokens , p ++ v . params }
125
- end )
126
- { tokens , params } = tokens ( right , file , length ( p ) , id )
127
- tokens = t ++ tokens
128
- plan ( tokens , id , module )
129
- struct ( SQL , params: cast_params ( params , p , binding ( ) ) , tokens: tokens , id: id , module: module )
132
+ case build ( left , right ) do
133
+ { :static , data } ->
134
+ { :ok , opts , _ , _ , _ , _ , tokens } = SQL.Lexer . lex ( data , env . file )
135
+ tokens = SQL . to_query ( SQL.Parser . parse ( tokens ) )
136
+ string = if mod = env . module do
137
+ SQL . to_string ( tokens , Module . get_attribute ( mod , :sql_adapter ) )
138
+ else
139
+ SQL . to_string ( tokens , SQL.Adapters.ANSI )
140
+ end
141
+ sql = struct ( SQL , tokens: tokens , string: string , module: env . module , inspect: data , id: id ( data ) )
142
+ quote bind_quoted: [ params: opts [ :binding ] , sql: Macro . escape ( sql ) ] do
143
+ % { sql | params: cast_params ( params , [ ] , binding ( ) ) }
144
+ end
145
+
146
+ { :dynamic , data } ->
147
+ sql = struct ( SQL , id: id ( data ) , module: env . module )
148
+ quote bind_quoted: [ left: Macro . unpipe ( left ) , right: right , file: env . file , data: data , sql: Macro . escape ( sql ) ] do
149
+ { t , p } = Enum . reduce ( left , { [ ] , [ ] } , fn
150
+ { [ ] , 0 } , acc -> acc
151
+ { v , 0 } , { t , p } -> { t ++ v . tokens , p ++ v . params }
152
+ end )
153
+ { tokens , params } = tokens ( right , file , length ( p ) , sql . id )
154
+ tokens = t ++ tokens
155
+ % { sql | params: cast_params ( params , p , binding ( ) ) , tokens: tokens , string: plan ( tokens , sql . id , sql . module ) , inspect: plan_inspect ( data , sql . id ) }
156
+ end
130
157
end
131
158
end
132
159
133
160
@ doc false
134
161
def build ( left , { :<<>> , _ , right } ) do
135
162
left
136
163
|> Macro . unpipe ( )
137
- |> Enum . reduce ( { :iodata , right } , fn
164
+ |> Enum . reduce ( { :static , right } , fn
138
165
{ [ ] , 0 } , acc -> acc
139
166
{ { :sigil_SQL , _meta , [ { :<<>> , _ , value } , [ ] ] } , 0 } , { type , acc } -> { type , [ value , ?\s , acc ] }
140
- { { _ , _ , _ } = var , 0 } , { _ , acc } ->
141
- { :dynamic , [ var , ?\s , acc ] }
167
+ { { _ , _ , _ } = var , 0 } , { _ , acc } -> { :dynamic , [ var , ?\s , acc ] }
142
168
end )
143
169
|> case do
144
- { :iodata , data } -> IO . iodata_to_binary ( data )
145
- { :dynamic , data } -> data
170
+ { :static , data } -> { :static , IO . iodata_to_binary ( data ) }
171
+ { :dynamic , data } -> { :dynamic , data }
146
172
end
147
173
end
148
174
@@ -181,31 +207,31 @@ defmodule SQL do
181
207
@ doc false
182
208
def plan ( tokens , id , module ) do
183
209
key = { module , id , :plan }
184
- if :persistent_term . get ( key , nil ) do
185
- id
210
+ if string = :persistent_term . get ( key , nil ) do
211
+ string
186
212
else
187
- :persistent_term . put ( key , to_string ( SQL . to_query ( SQL.Parser . parse ( tokens ) ) , module ) )
188
- id
213
+ string = to_string ( SQL . to_query ( SQL.Parser . parse ( tokens ) ) , module )
214
+ :persistent_term . put ( key , string )
215
+ string
189
216
end
190
217
end
191
218
192
219
@ doc false
193
220
def plan_inspect ( data , id ) do
194
221
key = { id , :inspect }
195
- if ! :persistent_term . get ( key , nil ) do
196
- data = case data do
197
- data when is_list ( data ) ->
198
- data
199
- |> Enum . map ( fn
200
- ast when is_struct ( ast ) -> :persistent_term . get ( { ast . id , :inspect } , nil )
201
- x -> x
202
- end )
203
- |> IO . iodata_to_binary ( )
204
-
205
- data -> data
206
- end
207
-
208
- :persistent_term . put ( key , data )
222
+ if inspect = :persistent_term . get ( key , nil ) do
223
+ inspect
224
+ else
225
+ inspect = data
226
+ |> Enum . map ( fn
227
+ ast when is_struct ( ast ) -> ast . inspect
228
+ x -> x
229
+ end )
230
+ |> IO . iodata_to_binary ( )
231
+
232
+
233
+ :persistent_term . put ( key , inspect )
234
+ inspect
209
235
end
210
236
end
211
237
end
0 commit comments