@@ -9,7 +9,6 @@ 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
1312
1413 @ doc """
1514 Converts a query to a list of joins.
@@ -300,110 +299,107 @@ defmodule Ecto.Query.Planner do
300299 defp normalize_subquery_select ( query , adapter , source? ) do
301300 { expr , % { select: select } = query } = rewrite_subquery_select_expr ( query , source? )
302301 { expr , _ } = prewalk ( expr , :select , query , select , 0 , adapter )
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 }
302+ { meta , _fields , _from } = collect_fields ( expr , [ ] , :never , query , select . take , true )
303+ { query , meta }
306304 end
307305
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 ) }
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 }
315311 end
316312
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
313+ defp rewrite_subquery_select_expr ( % { select: select } = query , source? ) do
314+ % { expr: expr , take: take } = select
324315
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
316+ expr =
317+ case subquery_select ( expr , take , query ) do
318+ { nil , fields } ->
319+ { :%{} , [ ] , fields }
328320
329- defp rewrite_subquery_select_expr ( query , _source? ) do
330- { query . select . expr , query }
331- end
321+ { struct , fields } ->
322+ { :% , [ ] , [ struct , { :%{} , [ ] , fields } ] }
332323
333- defp normalize_subquery_source ( { :map , left , extra } , rest , query ) do
334- normalize_subquery_source ( { :merge , left , { :map , extra } } , rest , query )
335- end
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 ) } `" )
336326
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 }
341- end
327+ :error ->
328+ expr
329+ end
342330
343- defp normalize_subquery_source ( { :source , _ , _ , types } = source , rest , _query ) do
344- { fields , rest } = zip_fields ( types , rest , [ ] )
345- { source , fields , rest }
331+ { expr , put_in ( query . select . expr , expr ) }
346332 end
347333
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
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 )
352337
353- defp normalize_subquery_source ( { :map , types } = map , rest , _query ) do
354- { fields , rest } = zip_fields ( types , rest , [ ] )
355- { map , fields , rest }
356- end
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
357341
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 ) } `" )
342+ { left_struct || right_struct , Keyword . merge ( left_fields , right_fields ) }
360343 end
344+ defp subquery_select ( { :% , _ , [ name , map ] } , take , query ) do
345+ { _ , fields } = subquery_select ( map , take , query )
346+ { name , fields }
347+ 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 )
361352
362- defp merge_subquery_source ( { :source , schema , prefix , types1 } , { :source , schema , prefix , types2 } , _query ) ,
363- do: { :source , schema , prefix , Keyword . merge ( types1 , types2 ) }
364-
365- defp merge_subquery_source ( { :source , schema , prefix , types } , { :map , fields } , _query ) ,
366- do: { :source , schema , prefix , merge_types_and_fields ( types , fields , [ ] ) }
367-
368- defp merge_subquery_source ( { :struct , name , fields1 } , { :struct , name , fields2 } , _query ) ,
369- do: { :struct , name , Keyword . merge ( fields1 , fields2 ) }
370-
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" )
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 )
379356
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 ] )
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" )
386360 end
387- end
388361
389- defp merge_types_and_fields ( types , [ ] , acc ) do
390- types ++ Enum . reverse ( acc )
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
391382 end
392383
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 )
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
401393
402- defp subquery_type_for ( { :struct , _name , types } , field ) ,
403- do: subquery_type_for_value ( types , field )
394+ defp subquery_fields ( fields , ix ) do
395+ for field <- fields do
396+ { field , { { :. , [ ] , [ { :& , [ ] , [ ix ] } , field ] } , [ ] , [ ] } }
397+ end
398+ end
404399
405- defp subquery_type_for ( { :map , types } , field ) ,
406- do: subquery_type_for_value ( types , field )
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 )
407403
408404 defp subquery_type_for_value ( types , field ) do
409405 case Keyword . fetch ( types , field ) do
@@ -413,6 +409,25 @@ defmodule Ecto.Query.Planner do
413409 end
414410 end
415411
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+
416431 defp plan_joins ( query , sources , offset , adapter ) do
417432 plan_joins ( query . joins , query , [ ] , sources , [ ] , 1 , offset , adapter )
418433 end
@@ -984,16 +999,17 @@ defmodule Ecto.Query.Planner do
984999
9851000 # We don't want to use normalize_subquery_select because we are
9861001 # going to prepare the whole query ourselves next.
987- { _expr , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
1002+ { _ , inner_query } = rewrite_subquery_select_expr ( inner_query , true )
9881003 { inner_query , counter } = traverse_exprs ( inner_query , :all , counter , fun )
9891004
9901005 # Now compute the fields as keyword lists so we emit AS in Ecto query.
9911006 % { select: % { expr: expr , take: take } } = inner_query
9921007 { source , fields , _from } = collect_fields ( expr , [ ] , :never , inner_query , take , true )
993- { _source , fields , [ ] } = normalize_subquery_source ( source , Enum . reverse ( fields ) , query )
1008+ { _ , keys } = subquery_struct_and_fields ( source )
1009+ inner_query = put_in ( inner_query . select . fields , Enum . zip ( keys , Enum . reverse ( fields ) ) )
9941010
995- inner_query = put_in ( inner_query . select . fields , fields )
9961011 { _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1012+
9971013 { [ { name , inner_query } | queries ] , counter }
9981014
9991015 { name , % QueryExpr { expr: { :fragment , _ , _ } = fragment } = query_expr } , { queries , counter } ->
@@ -1066,12 +1082,23 @@ defmodule Ecto.Query.Planner do
10661082 { fragments , acc } = prewalk ( fragments , kind , query , expr , acc , adapter )
10671083 { { :fragment , meta , fragments } , acc }
10681084 end
1069- defp prewalk_source ( % Ecto.SubQuery { query: inner_query } = subquery , _kind , query , _expr , counter , adapter ) do
1085+ defp prewalk_source ( % Ecto.SubQuery { query: inner_query } = subquery , kind , query , _expr , counter , adapter ) do
10701086 try do
10711087 inner_query = put_in inner_query . aliases [ @ parent_as ] , query
10721088 { inner_query , counter } = normalize_query ( inner_query , :all , adapter , counter )
10731089 { inner_query , _ } = normalize_select ( inner_query , true )
10741090 { _ , 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+
10751102 { % { subquery | query: inner_query } , counter }
10761103 rescue
10771104 e -> raise Ecto.SubQueryError , query: query , exception: e
@@ -1240,7 +1267,11 @@ defmodule Ecto.Query.Planner do
12401267 end
12411268 end
12421269
1243- defp normalize_select ( % { select: % { fields: nil } } = query , keep_literals? ) do
1270+ defp normalize_select ( % { select: nil } = query , _keep_literals? ) do
1271+ { query , nil }
1272+ end
1273+
1274+ defp normalize_select ( query , keep_literals? ) do
12441275 % { assocs: assocs , preloads: preloads , select: select } = query
12451276 % { take: take , expr: expr } = select
12461277 { tag , from_take } = Map . get ( take , 0 , { :any , [ ] } )
@@ -1288,10 +1319,6 @@ defmodule Ecto.Query.Planner do
12881319 { put_in ( query . select . fields , fields ) , select }
12891320 end
12901321
1291- defp normalize_select ( query , _keep_literals? ) do
1292- { query , nil }
1293- end
1294-
12951322 # Handling of source
12961323
12971324 defp collect_fields ( { :merge , _ , [ { :& , _ , [ 0 ] } , right ] } , fields , :none , query , take , keep_literals? ) do
@@ -1321,6 +1348,8 @@ defmodule Ecto.Query.Planner do
13211348
13221349 # Expression handling
13231350
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+
13241353 defp collect_fields ( { agg , _ , [ { { :. , dot_meta , [ { :& , _ , [ _ ] } , _ ] } , _ , [ ] } | _ ] } = expr ,
13251354 fields , from , _query , _take , _keep_literals? )
13261355 when agg in @ aggs do
@@ -1565,8 +1594,9 @@ defmodule Ecto.Query.Planner do
15651594 { types , fields } = select_dump ( schema . __schema__ ( :query_fields ) , schema . __schema__ ( :dump ) , ix )
15661595 { { :source , { source , schema } , prefix || query . prefix , types } , fields }
15671596
1568- { :error , % Ecto.SubQuery { select: select , query: inner_query } } ->
1569- { select , Enum . map ( inner_query . select . fields , & select_field ( elem ( & 1 , 0 ) , ix ) ) }
1597+ { :error , % Ecto.SubQuery { select: select } } ->
1598+ { _ , fields } = subquery_struct_and_fields ( select )
1599+ { select , Enum . map ( fields , & select_field ( & 1 , ix ) ) }
15701600 end
15711601 end
15721602
0 commit comments