@@ -9,6 +9,7 @@ defmodule Ecto.Query.Planner do
99 end
1010
1111 @ parent_as __MODULE__
12+ @ aggs ~w( count avg min max sum row_number rank dense_rank percent_rank cume_dist ntile lag lead first_value last_value nth_value) a
1213
1314 @ doc """
1415 Converts a query to a list of joins.
@@ -299,107 +300,110 @@ defmodule Ecto.Query.Planner do
299300 defp normalize_subquery_select ( query , adapter , source? ) do
300301 { expr , % { select: select } = query } = rewrite_subquery_select_expr ( query , source? )
301302 { expr , _ } = prewalk ( expr , :select , query , select , 0 , adapter )
302- { meta , _fields , _from } = collect_fields ( expr , [ ] , :never , query , select . take , true )
303- { query , meta }
303+ { source , fields , _from } = collect_fields ( expr , [ ] , :never , query , select . take , true )
304+ { source , fields , [ ] } = normalize_subquery_source ( source , Enum . reverse ( fields ) , query )
305+ { put_in ( query . select . fields , fields ) , source }
304306 end
305307
306- # If we are selecting a source, we keep it as is.
307- # Otherwise we normalize the select, which converts them into structs.
308- # This means that `select: p` in subqueries will be nullable in a join.
309- defp rewrite_subquery_select_expr ( % { select: % { expr: { :& , _ , [ _ ] } = expr } } = query , _source? ) do
310- { expr , query }
308+ # Convert single field lookups into a map
309+ defp rewrite_subquery_select_expr (
310+ % { select: % { expr: { { :. , _ , [ { :& , _ , [ _ ] } , field ] } , _ , [ ] } = expr } } = query ,
311+ _source?
312+ ) do
313+ expr = { :%{} , [ ] , [ { field , expr } ] }
314+ { expr , put_in ( query . select . expr , expr ) }
311315 end
312316
313- defp rewrite_subquery_select_expr ( % { select: select } = query , source? ) do
314- % { expr: expr , take: take } = select
315-
316- expr =
317- case subquery_select ( expr , take , query ) do
318- { nil , fields } ->
319- { :%{} , [ ] , fields }
317+ defp rewrite_subquery_select_expr (
318+ % { select: % { expr: { agg , _ , [ { { :. , _ , [ { :& , _ , [ _ ] } , field ] } , _ , [ ] } | _ ] } = expr } } = query ,
319+ _source?
320+ ) when agg in @ aggs do
321+ expr = { :%{} , [ ] , [ { field , expr } ] }
322+ { expr , put_in ( query . select . expr , expr ) }
323+ end
320324
321- { struct , fields } ->
322- { :% , [ ] , [ struct , { :%{} , [ ] , fields } ] }
325+ defp rewrite_subquery_select_expr ( query , false ) do
326+ error! ( query , "subquery must return a single field in order to be used on the right-side of `in`" )
327+ end
323328
324- :error when source? ->
325- error! ( query , "subquery/cte must select a source (t), a field (t.field) or a map, got: `#{ Macro . to_string ( expr ) } `" )
329+ defp rewrite_subquery_select_expr ( query , _source? ) do
330+ { query . select . expr , query }
331+ end
326332
327- :error ->
328- expr
329- end
333+ defp normalize_subquery_source ( { :map , left , extra } , rest , query ) do
334+ normalize_subquery_source ( { :merge , left , { :map , extra } } , rest , query )
335+ end
330336
331- { expr , put_in ( query . select . expr , expr ) }
337+ defp normalize_subquery_source ( { :merge , left , right } , rest , query ) do
338+ { left , left_fields , rest } = normalize_subquery_source ( left , rest , query )
339+ { right , right_fields , rest } = normalize_subquery_source ( right , rest , query )
340+ { merge_subquery_source ( left , right , query ) , Keyword . merge ( left_fields , right_fields ) , rest }
332341 end
333342
334- defp subquery_select ( { :merge , _ , [ left , right ] } , take , query ) do
335- { left_struct , left_fields } = subquery_select ( left , take , query )
336- { right_struct , right_fields } = subquery_select ( right , take , query )
343+ defp normalize_subquery_source ( { :source , _ , _ , types } = source , rest , _query ) do
344+ { fields , rest } = zip_fields ( types , rest , [ ] )
345+ { source , fields , rest }
346+ end
337347
338- unless is_nil ( left_struct ) or is_nil ( right_struct ) or left_struct == right_struct do
339- error! ( query , "cannot merge #{ inspect ( left_struct ) } and #{ inspect ( right_struct ) } because they are different structs" )
340- end
348+ defp normalize_subquery_source ( { :struct , _name , types } = struct , rest , _query ) do
349+ { fields , rest } = zip_fields ( types , rest , [ ] )
350+ { struct , fields , rest }
351+ end
341352
342- { left_struct || right_struct , Keyword . merge ( left_fields , right_fields ) }
353+ defp normalize_subquery_source ( { :map , types } = map , rest , _query ) do
354+ { fields , rest } = zip_fields ( types , rest , [ ] )
355+ { map , fields , rest }
343356 end
344- defp subquery_select ( { :% , _ , [ name , map ] } , take , query ) do
345- { _ , fields } = subquery_select ( map , take , query )
346- { name , fields }
357+
358+ defp normalize_subquery_source ( _source , _fields , query ) do
359+ error! ( query , "subquery/cte must select a source (t), a field (t.field) or a map, got: ` #{ Macro . to_string ( query . select . expr ) } `" )
347360 end
348- defp subquery_select ( { :%{} , _ , [ { :| , _ , [ { :& , [ ] , [ ix ] } , pairs ] } ] } = expr , take , query ) do
349- assert_subquery_fields! ( query , expr , pairs )
350- { source , _ } = source_take! ( :select , query , take , ix , ix )
351- { struct , fields } = subquery_struct_and_fields ( source )
352361
353- # Map updates may contain virtual fields, so we need to consider those
354- valid_keys = if struct , do: Map . keys ( struct . __struct__ ) , else: fields
355- update_keys = Keyword . keys ( pairs )
362+ defp merge_subquery_source ( { :source , schema , prefix , types1 } , { :source , schema , prefix , types2 } , _query ) ,
363+ do: { :source , schema , prefix , Keyword . merge ( types1 , types2 ) }
356364
357- case update_keys -- valid_keys do
358- [ ] -> :ok
359- [ key | _ ] -> error! ( query , "invalid key `#{ inspect key } ` for `#{ inspect struct } ` on map update in subquery/cte" )
360- end
365+ defp merge_subquery_source ( { :source , schema , prefix , types } , { :map , fields } , _query ) ,
366+ do: { :source , schema , prefix , merge_types_and_fields ( types , fields , [ ] ) }
361367
362- # In case of map updates, we need to remove duplicated fields
363- # at query time because we use the field names as aliases and
364- # duplicate aliases will lead to invalid queries.
365- kept_keys = fields -- update_keys
366- { struct , subquery_fields ( kept_keys , ix ) ++ pairs }
367- end
368- defp subquery_select ( { :%{} , _ , pairs } = expr , _take , query ) do
369- assert_subquery_fields! ( query , expr , pairs )
370- { nil , pairs }
371- end
372- defp subquery_select ( { :& , _ , [ ix ] } , take , query ) do
373- { source , _ } = source_take! ( :select , query , take , ix , ix )
374- { struct , fields } = subquery_struct_and_fields ( source )
375- { struct , subquery_fields ( fields , ix ) }
376- end
377- defp subquery_select ( { { :. , _ , [ { :& , _ , [ ix ] } , field ] } , _ , [ ] } , _take , _query ) do
378- { nil , subquery_fields ( [ field ] , ix ) }
379- end
380- defp subquery_select ( _expr , _take , _query ) do
381- :error
382- end
368+ defp merge_subquery_source ( { :struct , name , fields1 } , { :struct , name , fields2 } , _query ) ,
369+ do: { :struct , name , Keyword . merge ( fields1 , fields2 ) }
383370
384- defp subquery_struct_and_fields ( { :source , { _ , schema } , _ , types } ) do
385- { schema , Keyword . keys ( types ) }
386- end
387- defp subquery_struct_and_fields ( { :struct , name , types } ) do
388- { name , Keyword . keys ( types ) }
389- end
390- defp subquery_struct_and_fields ( { :map , types } ) do
391- { nil , Keyword . keys ( types ) }
392- end
371+ defp merge_subquery_source ( { :struct , name , fields1 } , { :map , fields2 } , _query ) ,
372+ do: { :struct , name , Keyword . merge ( fields1 , fields2 ) }
373+
374+ defp merge_subquery_source ( { :map , fields1 } , { :map , fields2 } , _query ) ,
375+ do: { :map , Keyword . merge ( fields1 , fields2 ) }
376+
377+ defp merge_subquery_source ( left , right , query ) ,
378+ do: error! ( query , "cannot merge #{ inspect ( left ) } and #{ inspect ( right ) } in subquery" )
393379
394- defp subquery_fields ( fields , ix ) do
395- for field <- fields do
396- { field , { { :. , [ ] , [ { :& , [ ] , [ ix ] } , field ] } , [ ] , [ ] } }
380+ # If the field exists in the schema, then its type is the same as in the schema.
381+ # If the field does not exist, then its type is any.
382+ defp merge_types_and_fields ( types , [ { field , _ } | extra ] , acc ) do
383+ case List . keytake ( types , field , 0 ) do
384+ { { field , type } , types } -> merge_types_and_fields ( types , extra , [ { field , type } | acc ] )
385+ nil -> merge_types_and_fields ( types , extra , [ { field , :any } | acc ] )
397386 end
398387 end
399388
400- defp subquery_type_for ( { :source , _ , _ , fields } , field ) , do: Keyword . fetch ( fields , field )
401- defp subquery_type_for ( { :struct , _name , types } , field ) , do: subquery_type_for_value ( types , field )
402- defp subquery_type_for ( { :map , types } , field ) , do: subquery_type_for_value ( types , field )
389+ defp merge_types_and_fields ( types , [ ] , acc ) do
390+ types ++ Enum . reverse ( acc )
391+ end
392+
393+ defp zip_fields ( [ { field , _type } | types ] , [ value | values ] , acc ) ,
394+ do: zip_fields ( types , values , [ { field , value } | acc ] )
395+
396+ defp zip_fields ( [ ] , values , acc ) ,
397+ do: { Enum . reverse ( acc ) , values }
398+
399+ defp subquery_type_for ( { :source , _ , _ , fields } , field ) ,
400+ do: Keyword . fetch ( fields , field )
401+
402+ defp subquery_type_for ( { :struct , _name , types } , field ) ,
403+ do: subquery_type_for_value ( types , field )
404+
405+ defp subquery_type_for ( { :map , types } , field ) ,
406+ do: subquery_type_for_value ( types , field )
403407
404408 defp subquery_type_for_value ( types , field ) do
405409 case Keyword . fetch ( types , field ) do
@@ -409,25 +413,6 @@ defmodule Ecto.Query.Planner do
409413 end
410414 end
411415
412- defp assert_subquery_fields! ( query , expr , pairs ) do
413- Enum . each ( pairs , fn
414- { key , _ } when not is_atom ( key ) ->
415- error! ( query , "only atom keys are allowed when selecting a map in subquery, got: `#{ Macro . to_string ( expr ) } `" )
416- { key , value } ->
417- if valid_subquery_value? ( value ) do
418- { key , value }
419- else
420- error! ( query , "maps, lists, tuples and sources are not allowed as map values in subquery, got: `#{ Macro . to_string ( expr ) } `" )
421- end
422- end )
423- end
424-
425- defp valid_subquery_value? ( { _ , _ } ) , do: false
426- defp valid_subquery_value? ( args ) when is_list ( args ) , do: false
427- defp valid_subquery_value? ( { container , _ , args } )
428- when container in [ :{} , :%{} , :& ] and is_list ( args ) , do: false
429- defp valid_subquery_value? ( _ ) , do: true
430-
431416 defp plan_joins ( query , sources , offset , adapter ) do
432417 plan_joins ( query . joins , query , [ ] , sources , [ ] , 1 , offset , adapter )
433418 end
@@ -999,17 +984,16 @@ defmodule Ecto.Query.Planner do
999984
1000985 # We don't want to use normalize_subquery_select because we are
1001986 # going to prepare the whole query ourselves next.
1002- { _ , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
987+ { _expr , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
1003988 { inner_query , counter } = traverse_exprs ( inner_query , :all , counter , fun )
1004989
1005990 # Now compute the fields as keyword lists so we emit AS in Ecto query.
1006991 % { select: % { expr: expr , take: take } } = inner_query
1007992 { source , fields , _from } = collect_fields ( expr , [ ] , :never , inner_query , take , true )
1008- { _ , keys } = subquery_struct_and_fields ( source )
1009- inner_query = put_in ( inner_query . select . fields , Enum . zip ( keys , Enum . reverse ( fields ) ) )
993+ { _source , fields , [ ] } = normalize_subquery_source ( source , Enum . reverse ( fields ) , query )
1010994
995+ inner_query = put_in ( inner_query . select . fields , fields )
1011996 { _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1012-
1013997 { [ { name , inner_query } | queries ] , counter }
1014998
1015999 { name , % QueryExpr { expr: { :fragment , _ , _ } = fragment } = query_expr } , { queries , counter } ->
@@ -1082,23 +1066,12 @@ defmodule Ecto.Query.Planner do
10821066 { fragments , acc } = prewalk ( fragments , kind , query , expr , acc , adapter )
10831067 { { :fragment , meta , fragments } , acc }
10841068 end
1085- defp prewalk_source ( % Ecto.SubQuery { query: inner_query } = subquery , kind , query , _expr , counter , adapter ) do
1069+ defp prewalk_source ( % Ecto.SubQuery { query: inner_query } = subquery , _kind , query , _expr , counter , adapter ) do
10861070 try do
10871071 inner_query = put_in inner_query . aliases [ @ parent_as ] , query
10881072 { inner_query , counter } = normalize_query ( inner_query , :all , adapter , counter )
10891073 { inner_query , _ } = normalize_select ( inner_query , true )
10901074 { _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1091-
1092- inner_query =
1093- # If the subquery comes from a select, we are not really interested on the fields
1094- if kind == :where do
1095- inner_query
1096- else
1097- update_in ( inner_query . select . fields , fn fields ->
1098- subquery . select |> subquery_struct_and_fields ( ) |> elem ( 1 ) |> Enum . zip ( fields )
1099- end )
1100- end
1101-
11021075 { % { subquery | query: inner_query } , counter }
11031076 rescue
11041077 e -> raise Ecto.SubQueryError , query: query , exception: e
@@ -1267,11 +1240,7 @@ defmodule Ecto.Query.Planner do
12671240 end
12681241 end
12691242
1270- defp normalize_select ( % { select: nil } = query , _keep_literals? ) do
1271- { query , nil }
1272- end
1273-
1274- defp normalize_select ( query , keep_literals? ) do
1243+ defp normalize_select ( % { select: % { fields: nil } } = query , keep_literals? ) do
12751244 % { assocs: assocs , preloads: preloads , select: select } = query
12761245 % { take: take , expr: expr } = select
12771246 { tag , from_take } = Map . get ( take , 0 , { :any , [ ] } )
@@ -1319,6 +1288,10 @@ defmodule Ecto.Query.Planner do
13191288 { put_in ( query . select . fields , fields ) , select }
13201289 end
13211290
1291+ defp normalize_select ( query , _keep_literals? ) do
1292+ { query , nil }
1293+ end
1294+
13221295 # Handling of source
13231296
13241297 defp collect_fields ( { :merge , _ , [ { :& , _ , [ 0 ] } , right ] } , fields , :none , query , take , keep_literals? ) do
@@ -1348,8 +1321,6 @@ defmodule Ecto.Query.Planner do
13481321
13491322 # Expression handling
13501323
1351- @ aggs ~w( count avg min max sum row_number rank dense_rank percent_rank cume_dist ntile lag lead first_value last_value nth_value) a
1352-
13531324 defp collect_fields ( { agg , _ , [ { { :. , dot_meta , [ { :& , _ , [ _ ] } , _ ] } , _ , [ ] } | _ ] } = expr ,
13541325 fields , from , _query , _take , _keep_literals? )
13551326 when agg in @ aggs do
@@ -1594,9 +1565,8 @@ defmodule Ecto.Query.Planner do
15941565 { types , fields } = select_dump ( schema . __schema__ ( :query_fields ) , schema . __schema__ ( :dump ) , ix )
15951566 { { :source , { source , schema } , prefix || query . prefix , types } , fields }
15961567
1597- { :error , % Ecto.SubQuery { select: select } } ->
1598- { _ , fields } = subquery_struct_and_fields ( select )
1599- { select , Enum . map ( fields , & select_field ( & 1 , ix ) ) }
1568+ { :error , % Ecto.SubQuery { select: select , query: inner_query } } ->
1569+ { select , Enum . map ( inner_query . select . fields , & select_field ( elem ( & 1 , 0 ) , ix ) ) }
16001570 end
16011571 end
16021572
0 commit comments