@@ -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.
@@ -297,48 +298,39 @@ defmodule Ecto.Query.Planner do
297298 end
298299
299300 defp normalize_subquery_select ( query , adapter , source? ) do
300- { expr , % { select: select } = query } = rewrite_subquery_select_expr ( query , source? )
301+ { schema_or_source , 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+ { { :map , types } , _fields , _from } = collect_fields ( expr , [ ] , :never , query , select . take , true )
304+ { query , subquery_source ( schema_or_source , types ) }
304305 end
305306
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 }
307+ defp subquery_source ( nil , types ) , do: { :map , types }
308+ defp subquery_source ( name , types ) when is_atom ( name ) , do: { :struct , name , types }
309+ defp subquery_source ( { :source , schema , prefix , types } , only ) do
310+ types = Enum . map ( only , fn { field , _ } -> { field , Keyword . get ( types , field , :any ) } end )
311+ { :source , schema , prefix , types }
311312 end
312313
313314 defp rewrite_subquery_select_expr ( % { select: select } = query , source? ) do
314315 % { expr: expr , take: take } = select
315316
316- expr =
317- case subquery_select ( expr , take , query ) do
318- { nil , fields } ->
319- { :%{} , [ ] , fields }
320-
321- { struct , fields } ->
322- { :% , [ ] , [ struct , { :%{} , [ ] , fields } ] }
317+ case subquery_select ( expr , take , query ) do
318+ { schema_or_source , fields } ->
319+ expr = { :%{} , [ ] , fields }
320+ { schema_or_source , expr , put_in ( query . select . expr , expr ) }
323321
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 ) } `" )
322+ :error when source? ->
323+ error! ( query , "subquery/cte must select a source (t), a field (t.field) or a map, got: `#{ Macro . to_string ( expr ) } `" )
326324
327- :error ->
328- expr
329- end
330-
331- { expr , put_in ( query . select . expr , expr ) }
325+ :error ->
326+ expr = { :%{} , [ ] , [ result: expr ] }
327+ { nil , expr , put_in ( query . select . expr , expr ) }
328+ end
332329 end
333330
334331 defp subquery_select ( { :merge , _ , [ left , right ] } , take , query ) do
335332 { left_struct , left_fields } = subquery_select ( left , take , query )
336333 { right_struct , right_fields } = subquery_select ( right , take , query )
337-
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
341-
342334 { left_struct || right_struct , Keyword . merge ( left_fields , right_fields ) }
343335 end
344336 defp subquery_select ( { :% , _ , [ name , map ] } , take , query ) do
@@ -348,55 +340,43 @@ defmodule Ecto.Query.Planner do
348340 defp subquery_select ( { :%{} , _ , [ { :| , _ , [ { :& , [ ] , [ ix ] } , pairs ] } ] } = expr , take , query ) do
349341 assert_subquery_fields! ( query , expr , pairs )
350342 { source , _ } = source_take! ( :select , query , take , ix , ix )
351- { struct , fields } = subquery_struct_and_fields ( source )
352-
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 )
356-
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
361343
362344 # In case of map updates, we need to remove duplicated fields
363345 # at query time because we use the field names as aliases and
364346 # duplicate aliases will lead to invalid queries.
365- kept_keys = fields -- update_keys
366- { struct , subquery_fields ( kept_keys , ix ) ++ pairs }
347+ kept_keys = subquery_source_fields ( source ) -- Keyword . keys ( pairs )
348+ { keep_source_or_struct ( source ) , subquery_fields ( kept_keys , ix ) ++ pairs }
367349 end
368350 defp subquery_select ( { :%{} , _ , pairs } = expr , _take , query ) do
369351 assert_subquery_fields! ( query , expr , pairs )
370352 { nil , pairs }
371353 end
372354 defp subquery_select ( { :& , _ , [ ix ] } , take , query ) do
373355 { source , _ } = source_take! ( :select , query , take , ix , ix )
374- { struct , fields } = subquery_struct_and_fields ( source )
375- { struct , subquery_fields ( fields , ix ) }
356+ fields = subquery_source_fields ( source )
357+ { keep_source_or_struct ( source ) , subquery_fields ( fields , ix ) }
376358 end
377- defp subquery_select ( { { :. , _ , [ { :& , _ , [ ix ] } , field ] } , _ , [ ] } , _take , _query ) do
378- { nil , subquery_fields ( [ field ] , ix ) }
359+ defp subquery_select ( { { :. , _ , [ { :& , _ , [ _ ] } , field ] } , _ , [ ] } = expr , _take , _query ) do
360+ { nil , [ { field , expr } ] }
379361 end
380362 defp subquery_select ( _expr , _take , _query ) do
381363 :error
382364 end
383365
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
393-
394366 defp subquery_fields ( fields , ix ) do
395367 for field <- fields do
396368 { field , { { :. , [ ] , [ { :& , [ ] , [ ix ] } , field ] } , [ ] , [ ] } }
397369 end
398370 end
399371
372+ defp keep_source_or_struct ( { :source , _ , _ , _ } = source ) , do: source
373+ defp keep_source_or_struct ( { :struct , name , _ } ) , do: name
374+ defp keep_source_or_struct ( _ ) , do: nil
375+
376+ defp subquery_source_fields ( { :source , _ , _ , types } ) , do: Keyword . keys ( types )
377+ defp subquery_source_fields ( { :struct , _ , types } ) , do: Keyword . keys ( types )
378+ defp subquery_source_fields ( { :map , types } ) , do: Keyword . keys ( types )
379+
400380 defp subquery_type_for ( { :source , _ , _ , fields } , field ) , do: Keyword . fetch ( fields , field )
401381 defp subquery_type_for ( { :struct , _name , types } , field ) , do: subquery_type_for_value ( types , field )
402382 defp subquery_type_for ( { :map , types } , field ) , do: subquery_type_for_value ( types , field )
@@ -413,6 +393,7 @@ defmodule Ecto.Query.Planner do
413393 Enum . each ( pairs , fn
414394 { key , _ } when not is_atom ( key ) ->
415395 error! ( query , "only atom keys are allowed when selecting a map in subquery, got: `#{ Macro . to_string ( expr ) } `" )
396+
416397 { key , value } ->
417398 if valid_subquery_value? ( value ) do
418399 { key , value }
@@ -999,15 +980,14 @@ defmodule Ecto.Query.Planner do
999980
1000981 # We don't want to use normalize_subquery_select because we are
1001982 # going to prepare the whole query ourselves next.
1002- { _ , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
983+ { _ , _ , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
1003984 { inner_query , counter } = traverse_exprs ( inner_query , :all , counter , fun )
1004985
1005986 # Now compute the fields as keyword lists so we emit AS in Ecto query.
1006987 % { select: % { expr: expr , take: take } } = inner_query
1007- { 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 ) ) )
1010-
988+ { { :map , types } , fields , _from } = collect_fields ( expr , [ ] , :never , inner_query , take , true )
989+ fields = Enum . zip ( Keyword . keys ( types ) , Enum . reverse ( fields ) )
990+ inner_query = put_in ( inner_query . select . fields , fields )
1011991 { _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1012992
1013993 { [ { name , inner_query } | queries ] , counter }
@@ -1095,7 +1075,7 @@ defmodule Ecto.Query.Planner do
10951075 inner_query
10961076 else
10971077 update_in ( inner_query . select . fields , fn fields ->
1098- subquery . select |> subquery_struct_and_fields ( ) |> elem ( 1 ) |> Enum . zip ( fields )
1078+ subquery . select |> subquery_source_fields ( ) |> Enum . zip ( fields )
10991079 end )
11001080 end
11011081
@@ -1348,8 +1328,6 @@ defmodule Ecto.Query.Planner do
13481328
13491329 # Expression handling
13501330
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-
13531331 defp collect_fields ( { agg , _ , [ { { :. , dot_meta , [ { :& , _ , [ _ ] } , _ ] } , _ , [ ] } | _ ] } = expr ,
13541332 fields , from , _query , _take , _keep_literals? )
13551333 when agg in @ aggs do
@@ -1595,7 +1573,7 @@ defmodule Ecto.Query.Planner do
15951573 { { :source , { source , schema } , prefix || query . prefix , types } , fields }
15961574
15971575 { :error , % Ecto.SubQuery { select: select } } ->
1598- { _ , fields } = subquery_struct_and_fields ( select )
1576+ fields = subquery_source_fields ( select )
15991577 { select , Enum . map ( fields , & select_field ( & 1 , ix ) ) }
16001578 end
16011579 end
0 commit comments