@@ -38,48 +38,7 @@ defmodule SQL do
38
38
{"select id, email from users where email = $0", ["[email protected] "]}
39
39
"""
40
40
@ doc since: "0.1.0"
41
- def to_sql ( sql ) , do: { :persistent_term . get ( sql . id ) , sql . params }
42
-
43
- @ doc false
44
- def build ( right , { :<<>> , meta , _ } = left , _modifiers , env ) do
45
- quote bind_quoted: [ right: Macro . unpipe ( right ) , left: left , meta: Macro . escape ( { meta [ :line ] , meta [ :column ] || 0 , env . file } ) , e: Macro . escape ( env ) ] do
46
- { t , p } = Enum . reduce ( right , { [ ] , [ ] } , fn
47
- { [ ] , 0 } , acc -> acc
48
- { v , 0 } , { tokens , params } -> { tokens ++ v . tokens , params ++ v . params }
49
- end )
50
- binding = binding ( )
51
- id = { __MODULE__ , :binary . decode_unsigned ( left ) , meta }
52
- { tokens , params } = tokens ( left , meta , length ( p ) , id )
53
- tokens = t ++ tokens
54
- params = Enum . reduce ( params , p , fn
55
- { :var , var } , acc -> acc ++ [ binding [ String . to_atom ( var ) ] ]
56
- { :code , code } , acc -> acc ++ [ elem ( Code . eval_string ( code , binding ) , 0 ) ]
57
- end )
58
- struct ( SQL , params: params , tokens: tokens , id: plan ( id , tokens ) , module: __MODULE__ )
59
- end
60
- end
61
-
62
- def tokens ( left , meta , p , id ) do
63
- if result = :persistent_term . get ( id , nil ) do
64
- result
65
- else
66
- { :ok , opts , _ , _ , _ , _ , tokens } = SQL.Lexer . lex ( left , meta , p )
67
- result = { tokens , opts [ :binding ] }
68
- :persistent_term . put ( id , result )
69
- result
70
- end
71
- end
72
-
73
- def plan ( id , tokens ) do
74
- if uid = :persistent_term . get ( tokens , nil ) do
75
- uid
76
- else
77
- uid = System . unique_integer ( [ :positive ] )
78
- :persistent_term . put ( tokens , uid )
79
- :persistent_term . put ( uid , to_string ( SQL . to_query ( SQL.Parser . parse ( tokens ) ) , elem ( id , 0 ) ) )
80
- uid
81
- end
82
- end
41
+ def to_sql ( % { params: params , id: id , module: module } ) , do: { :persistent_term . get ( { module , id , :plan } ) , params }
83
42
84
43
@ doc """
85
44
Handles the sigil `~SQL` for SQL.
@@ -124,7 +83,16 @@ defmodule SQL do
124
83
token
125
84
end
126
85
86
+ defimpl Inspect , for: SQL do
87
+ def inspect ( sql , _opts ) , do: Inspect.Algebra . concat ( [ "~SQL\" \" \" \n " , :persistent_term . get ( { sql . id , :inspect } ) , "\n \" \" \" " ] )
88
+ end
89
+
90
+ defimpl String.Chars , for: SQL do
91
+ def to_string ( % { id: id , module: module } ) , do: :persistent_term . get ( { module , id , :plan } )
92
+ def to_string ( % { tokens: tokens , module: module } ) , do: SQL . to_string ( tokens , module )
93
+ end
127
94
95
+ @ doc false
128
96
def to_string ( tokens , module ) do
129
97
fun = cond do
130
98
Kernel . function_exported? ( module , :sql_config , 0 ) -> & module . sql_config ( ) [ :adapter ] . token_to_string ( & 1 )
@@ -143,19 +111,99 @@ defmodule SQL do
143
111
|> IO . iodata_to_binary ( )
144
112
end
145
113
114
+ @ doc false
115
+ def build ( left , { :<<>> , _ , _ } = right , _modifiers , env ) do
116
+ data = build ( left , right )
117
+ quote bind_quoted: [ module: env . module , left: Macro . unpipe ( left ) , right: right , file: env . file , id: id ( data ) , data: data ] do
118
+ plan_inspect ( data , id )
119
+ { t , p } = Enum . reduce ( left , { [ ] , [ ] } , fn
120
+ { [ ] , 0 } , acc -> acc
121
+ { v , 0 } , { t , p } ->
122
+ { t ++ v . tokens , p ++ v . params }
123
+ end )
124
+ { tokens , params } = tokens ( right , file , length ( p ) , id )
125
+ tokens = t ++ tokens
126
+ plan ( tokens , id , module )
127
+ struct ( SQL , params: cast_params ( params , p , binding ( ) ) , tokens: tokens , id: id , module: module )
128
+ end
129
+ end
130
+
131
+ @ doc false
132
+ def build ( left , { :<<>> , _ , right } ) do
133
+ left
134
+ |> Macro . unpipe ( )
135
+ |> Enum . reduce ( { :iodata , right } , fn
136
+ { [ ] , 0 } , acc -> acc
137
+ { { :sigil_SQL , _meta , [ { :<<>> , _ , value } , [ ] ] } , 0 } , { type , acc } -> { type , [ value , ?\s , acc ] }
138
+ { { _ , _ , _ } = var , 0 } , { _ , acc } ->
139
+ { :dynamic , [ var , ?\s , acc ] }
140
+ end )
141
+ |> case do
142
+ { :iodata , data } -> IO . iodata_to_binary ( data )
143
+ { :dynamic , data } -> data
144
+ end
145
+ end
146
146
147
- defimpl Inspect , for: SQL do
148
- def inspect ( sql , _opts ) do
149
- if Kernel . function_exported? ( sql . module , :sql_config , 0 ) do
150
- Enum . reduce ( 0 .. length ( sql . params ) , :persistent_term . get ( sql . id ) , & String . replace ( & 2 , sql . module . sql_config ( ) [ :adapter ] . token_to_string ( { :binding , [ ] , [ & 1 ] } ) , sql . module . sql_config ( ) [ :adapter ] . token_to_string ( Enum . at ( sql . params , & 1 ) ) , global: false ) )
151
- else
152
- Enum . reduce ( 0 .. length ( sql . params ) , :persistent_term . get ( sql . id ) , & String . replace ( & 2 , SQL.String . token_to_sql ( { :binding , [ ] , [ & 1 ] } ) , SQL.String . token_to_sql ( Enum . at ( sql . params , & 1 ) ) ) )
153
- end
147
+ @ doc false
148
+ def id ( data ) do
149
+ if id = :persistent_term . get ( data , nil ) do
150
+ id
151
+ else
152
+ id = System . unique_integer ( [ :positive ] )
153
+ :persistent_term . put ( data , id )
154
+ id
154
155
end
155
156
end
156
157
157
- defimpl String.Chars , for: SQL do
158
- def to_string ( % { id: id } ) , do: :persistent_term . get ( id )
159
- def to_string ( % { tokens: tokens , module: module } ) , do: SQL . to_string ( tokens , module )
158
+ @ doc false
159
+ def cast_params ( bindings , params , binding ) do
160
+ Enum . reduce ( bindings , params , fn
161
+ { :var , var } , acc -> if v = binding [ String . to_atom ( var ) ] , do: acc ++ [ v ] , else: acc
162
+ { :code , code } , acc -> acc ++ [ elem ( Code . eval_string ( code , binding ) , 0 ) ]
163
+ end )
164
+ end
165
+
166
+ @ doc false
167
+ def tokens ( binary , file , count , id ) do
168
+ key = { id , :lex }
169
+ if result = :persistent_term . get ( key , nil ) do
170
+ result
171
+ else
172
+ { :ok , opts , _ , _ , _ , _ , tokens } = SQL.Lexer . lex ( binary , file , count )
173
+ result = { tokens , opts [ :binding ] }
174
+ :persistent_term . put ( key , result )
175
+ result
176
+ end
177
+ end
178
+
179
+ @ doc false
180
+ def plan ( tokens , id , module ) do
181
+ key = { module , id , :plan }
182
+ if :persistent_term . get ( key , nil ) do
183
+ id
184
+ else
185
+ :persistent_term . put ( key , to_string ( SQL . to_query ( SQL.Parser . parse ( tokens ) ) , module ) )
186
+ id
187
+ end
188
+ end
189
+
190
+ @ doc false
191
+ def plan_inspect ( data , id ) do
192
+ key = { id , :inspect }
193
+ if ! :persistent_term . get ( key , nil ) do
194
+ data = case data do
195
+ data when is_list ( data ) ->
196
+ data
197
+ |> Enum . map ( fn
198
+ ast when is_struct ( ast ) -> :persistent_term . get ( { ast . id , :inspect } , nil )
199
+ x -> x
200
+ end )
201
+ |> IO . iodata_to_binary ( )
202
+
203
+ data -> data
204
+ end
205
+
206
+ :persistent_term . put ( key , data )
207
+ end
160
208
end
161
209
end
0 commit comments