@@ -9,6 +9,7 @@ defmodule Ecto.Query.Planner do
9
9
end
10
10
11
11
@ 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
12
13
13
14
@ doc """
14
15
Converts a query to a list of joins.
@@ -297,48 +298,39 @@ defmodule Ecto.Query.Planner do
297
298
end
298
299
299
300
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? )
301
302
{ 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 ) }
304
305
end
305
306
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 }
311
312
end
312
313
313
314
defp rewrite_subquery_select_expr ( % { select: select } = query , source? ) do
314
315
% { expr: expr , take: take } = select
315
316
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 ) }
323
321
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 ) } `" )
326
324
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
332
329
end
333
330
334
331
defp subquery_select ( { :merge , _ , [ left , right ] } , take , query ) do
335
332
{ left_struct , left_fields } = subquery_select ( left , take , query )
336
333
{ 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
-
342
334
{ left_struct || right_struct , Keyword . merge ( left_fields , right_fields ) }
343
335
end
344
336
defp subquery_select ( { :% , _ , [ name , map ] } , take , query ) do
@@ -348,55 +340,43 @@ defmodule Ecto.Query.Planner do
348
340
defp subquery_select ( { :%{} , _ , [ { :| , _ , [ { :& , [ ] , [ ix ] } , pairs ] } ] } = expr , take , query ) do
349
341
assert_subquery_fields! ( query , expr , pairs )
350
342
{ 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
361
343
362
344
# In case of map updates, we need to remove duplicated fields
363
345
# at query time because we use the field names as aliases and
364
346
# 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 }
367
349
end
368
350
defp subquery_select ( { :%{} , _ , pairs } = expr , _take , query ) do
369
351
assert_subquery_fields! ( query , expr , pairs )
370
352
{ nil , pairs }
371
353
end
372
354
defp subquery_select ( { :& , _ , [ ix ] } , take , query ) do
373
355
{ 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 ) }
376
358
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 } ] }
379
361
end
380
362
defp subquery_select ( _expr , _take , _query ) do
381
363
:error
382
364
end
383
365
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
-
394
366
defp subquery_fields ( fields , ix ) do
395
367
for field <- fields do
396
368
{ field , { { :. , [ ] , [ { :& , [ ] , [ ix ] } , field ] } , [ ] , [ ] } }
397
369
end
398
370
end
399
371
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
+
400
380
defp subquery_type_for ( { :source , _ , _ , fields } , field ) , do: Keyword . fetch ( fields , field )
401
381
defp subquery_type_for ( { :struct , _name , types } , field ) , do: subquery_type_for_value ( types , field )
402
382
defp subquery_type_for ( { :map , types } , field ) , do: subquery_type_for_value ( types , field )
@@ -413,6 +393,7 @@ defmodule Ecto.Query.Planner do
413
393
Enum . each ( pairs , fn
414
394
{ key , _ } when not is_atom ( key ) ->
415
395
error! ( query , "only atom keys are allowed when selecting a map in subquery, got: `#{ Macro . to_string ( expr ) } `" )
396
+
416
397
{ key , value } ->
417
398
if valid_subquery_value? ( value ) do
418
399
{ key , value }
@@ -999,15 +980,14 @@ defmodule Ecto.Query.Planner do
999
980
1000
981
# We don't want to use normalize_subquery_select because we are
1001
982
# 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 )
1003
984
{ inner_query , counter } = traverse_exprs ( inner_query , :all , counter , fun )
1004
985
1005
986
# Now compute the fields as keyword lists so we emit AS in Ecto query.
1006
987
% { 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 )
1011
991
{ _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1012
992
1013
993
{ [ { name , inner_query } | queries ] , counter }
@@ -1095,7 +1075,7 @@ defmodule Ecto.Query.Planner do
1095
1075
inner_query
1096
1076
else
1097
1077
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 )
1099
1079
end )
1100
1080
end
1101
1081
@@ -1348,8 +1328,6 @@ defmodule Ecto.Query.Planner do
1348
1328
1349
1329
# Expression handling
1350
1330
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
-
1353
1331
defp collect_fields ( { agg , _ , [ { { :. , dot_meta , [ { :& , _ , [ _ ] } , _ ] } , _ , [ ] } | _ ] } = expr ,
1354
1332
fields , from , _query , _take , _keep_literals? )
1355
1333
when agg in @ aggs do
@@ -1595,7 +1573,7 @@ defmodule Ecto.Query.Planner do
1595
1573
{ { :source , { source , schema } , prefix || query . prefix , types } , fields }
1596
1574
1597
1575
{ :error , % Ecto.SubQuery { select: select } } ->
1598
- { _ , fields } = subquery_struct_and_fields ( select )
1576
+ fields = subquery_source_fields ( select )
1599
1577
{ select , Enum . map ( fields , & select_field ( & 1 , ix ) ) }
1600
1578
end
1601
1579
end
0 commit comments