@@ -8,11 +8,14 @@ defmodule SQL do
88 |> Enum . fetch! ( 1 )
99 @ moduledoc since: "0.1.0"
1010
11+ @ adapters [ SQL.Adapters.ANSI , SQL.Adapters.MySQL , SQL.Adapters.Postgres , SQL.Adapters.TDS ]
12+
1113 defmacro __using__ ( opts ) do
1214 quote bind_quoted: [ opts: opts ] do
1315 @ doc false
1416 @ behaviour SQL
1517 import SQL
18+ @ sql_adapter opts [ :adapter ]
1619 def sql_config , do: unquote ( opts )
1720 def token_to_sql ( token ) , do: token_to_sql ( token )
1821 defoverridable token_to_sql: 1
@@ -27,7 +30,15 @@ defmodule SQL do
2730 @ doc deprecated: "Use SQL.Token.token_to_string/1 instead"
2831 @ callback token_to_sql ( token :: { atom , keyword , list } ) :: String . t ( )
2932
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
3142
3243 @ doc """
3344 Returns a parameterized SQL.
@@ -38,7 +49,7 @@ defmodule SQL do
3849 {"select id, email from users where email = ?", ["john@example.com"]}
3950 """
4051 @ 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 }
4253
4354 @ doc """
4455 Handles the sigil `~SQL` for SQL.
@@ -59,11 +70,11 @@ defmodule SQL do
5970 @ doc false
6071 @ doc since: "0.1.0"
6172 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 ] )
6374 tokens
6475 |> SQL.Parser . parse ( )
6576 |> to_query ( )
66- |> to_string ( SQL.String )
77+ |> to_string ( SQL.Adapters.ANSI )
6778 end
6879
6980 @ doc false
@@ -85,16 +96,19 @@ defmodule SQL do
8596 token
8697 end
8798
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-
9799 @ 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
98112 def to_string ( tokens , module ) do
99113 fun = cond do
100114 Kernel . function_exported? ( module , :sql_config , 0 ) -> & module . sql_config ( ) [ :adapter ] . token_to_string ( & 1 )
@@ -115,34 +129,46 @@ defmodule SQL do
115129
116130 @ doc false
117131 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
130157 end
131158 end
132159
133160 @ doc false
134161 def build ( left , { :<<>> , _ , right } ) do
135162 left
136163 |> Macro . unpipe ( )
137- |> Enum . reduce ( { :iodata , right } , fn
164+ |> Enum . reduce ( { :static , right } , fn
138165 { [ ] , 0 } , acc -> acc
139166 { { :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 ] }
142168 end )
143169 |> 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 }
146172 end
147173 end
148174
@@ -181,31 +207,31 @@ defmodule SQL do
181207 @ doc false
182208 def plan ( tokens , id , module ) do
183209 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
186212 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
189216 end
190217 end
191218
192219 @ doc false
193220 def plan_inspect ( data , id ) do
194221 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
209235 end
210236 end
211237end
0 commit comments