@@ -23,7 +23,7 @@ defmodule SQL do
2323 end
2424 end
2525
26- defstruct [ tokens: [ ] , params: [ ] , module: nil , id: nil , string: nil , inspect: nil ]
26+ defstruct [ tokens: [ ] , params: [ ] , module: nil , id: nil , string: nil , inspect: nil , fn: nil ]
2727
2828 defimpl Inspect , for: SQL do
2929 def inspect ( sql , _opts ) , do: sql . inspect
@@ -64,6 +64,24 @@ defmodule SQL do
6464 SQL . build ( left , right , modifiers , __CALLER__ )
6565 end
6666
67+ @ doc """
68+ Perform transformation on the result set.
69+
70+ ## Examples
71+ iex(1)> SQL.map(~SQL"from users select id, email", fn row, columns -> Map.new(Enum.zip(columns, row)) end)
72+ ~SQL\" \" \"
73+ select
74+ id,
75+ email
76+ from
77+ users
78+ \" \" \"
79+ """
80+ @ doc since: "0.4.0"
81+ defmacro map ( left , right ) do
82+ SQL . build ( left , right , __CALLER__ )
83+ end
84+
6785 @ doc false
6886 @ doc since: "0.1.0"
6987 def parse ( binary , opts \\ [ format: true , module: ANSI , sql_lock: nil ] ) do
@@ -114,6 +132,23 @@ defmodule SQL do
114132 end
115133 end
116134
135+ @ doc false
136+ def build ( left , { tag , _ , _ } = right , _env ) when tag in ~w[ fn &] a do
137+ { _type , data , acc2 } = left
138+ |> Macro . unpipe ( )
139+ |> Enum . reduce ( { :static , [ ] , [ ] } , fn
140+ { [ ] , 0 } , acc -> acc
141+ { { _ , _ , [ ] } = r , 0 } , { _ , l , right } -> { :dynamic , Macro . pipe ( l , r , 0 ) , right }
142+ { { :sigil_SQL , _meta , [ { :<<>> , _ , _ } , [ ] ] } = r , 0 } , { type , l , right } -> { type , Macro . pipe ( l , r , 0 ) , right }
143+ { { { :. , _ , [ { _ , _ , [ :SQL ] } , :map ] } , _ , [ left ] } , 0 } , { type , acc , acc2 } -> { type , acc , [ left | acc2 ] }
144+ end )
145+ [ r | rest ] = Enum . reverse ( [ right | acc2 ] )
146+ right = Enum . reduce ( rest , r , fn r , { t , m , [ { t2 , m2 , [ vars , block ] } ] } -> { t , m , [ { t2 , m2 , [ vars , quote ( do: unquote ( r ) . ( unquote ( block ) ) ) ] } ] } end )
147+ quote bind_quoted: [ left: data , right: right ] do
148+ % { left | fn: right }
149+ end
150+ end
151+
117152 @ doc false
118153 def build ( left , { :<<>> , _ , right } ) do
119154 left
@@ -244,4 +279,26 @@ defmodule SQL do
244279 IO . warn ( IO.ANSI . format ( [ ?\n , format_error ( errors ) , iodata ] ) , [ stack | t ] )
245280 { IO . iodata_to_binary ( module . token_to_string ( tokens ) ) , ~s[ ~SQL"""\n #{ IO . iodata_to_binary ( IO.ANSI . format ( iodata , false ) ) } """] }
246281 end
282+
283+ defimpl Enumerable , for: SQL do
284+ def count ( _enumerable ) do
285+ { :error , __MODULE__ }
286+ end
287+ def member? ( _enumerable , _element ) do
288+ { :error , __MODULE__ }
289+ end
290+ def reduce ( % SQL { } = enumerable , acc , fun ) do
291+ repo = enumerable . module . sql_config ( ) [ :repo ]
292+ % { rows: rows , columns: columns } = repo . query! ( enumerable . string , enumerable . params )
293+ fun = case Function . info ( enumerable . fn , :arity ) do
294+ { :arity , 1 } -> fn row , acc -> fun . ( enumerable . fn . ( row ) , acc ) end
295+ { :arity , 2 } -> fn row , acc -> fun . ( enumerable . fn . ( row , columns ) , acc ) end
296+ { :arity , 3 } -> fn row , acc -> fun . ( enumerable . fn . ( row , columns , repo ) , acc ) end
297+ end
298+ Enumerable . reduce ( rows , acc , fun )
299+ end
300+ def slice ( _enumerable ) do
301+ { :error , __MODULE__ }
302+ end
303+ end
247304end
0 commit comments