@@ -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.
@@ -299,107 +300,110 @@ defmodule Ecto.Query.Planner do
299
300
defp normalize_subquery_select ( query , adapter , source? ) do
300
301
{ 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
+ { 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 }
304
306
end
305
307
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 ) }
311
315
end
312
316
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
320
324
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
323
328
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
326
332
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
330
336
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 }
332
341
end
333
342
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
337
347
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
341
352
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 }
343
356
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 ) } `" )
347
360
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 )
352
361
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 ) }
356
364
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 , [ ] ) }
361
367
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 ) }
383
370
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" )
393
379
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 ] )
397
386
end
398
387
end
399
388
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 )
403
407
404
408
defp subquery_type_for_value ( types , field ) do
405
409
case Keyword . fetch ( types , field ) do
@@ -409,25 +413,6 @@ defmodule Ecto.Query.Planner do
409
413
end
410
414
end
411
415
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
-
431
416
defp plan_joins ( query , sources , offset , adapter ) do
432
417
plan_joins ( query . joins , query , [ ] , sources , [ ] , 1 , offset , adapter )
433
418
end
@@ -999,17 +984,16 @@ defmodule Ecto.Query.Planner do
999
984
1000
985
# We don't want to use normalize_subquery_select because we are
1001
986
# 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 )
1003
988
{ inner_query , counter } = traverse_exprs ( inner_query , :all , counter , fun )
1004
989
1005
990
# Now compute the fields as keyword lists so we emit AS in Ecto query.
1006
991
% { select: % { expr: expr , take: take } } = inner_query
1007
992
{ 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 )
1010
994
995
+ inner_query = put_in ( inner_query . select . fields , fields )
1011
996
{ _ , inner_query } = pop_in ( inner_query . aliases [ @ parent_as ] )
1012
-
1013
997
{ [ { name , inner_query } | queries ] , counter }
1014
998
1015
999
{ name , % QueryExpr { expr: { :fragment , _ , _ } = fragment } = query_expr } , { queries , counter } ->
@@ -1082,23 +1066,12 @@ defmodule Ecto.Query.Planner do
1082
1066
{ fragments , acc } = prewalk ( fragments , kind , query , expr , acc , adapter )
1083
1067
{ { :fragment , meta , fragments } , acc }
1084
1068
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
1086
1070
try do
1087
1071
inner_query = put_in inner_query . aliases [ @ parent_as ] , query
1088
1072
{ inner_query , counter } = normalize_query ( inner_query , :all , adapter , counter )
1089
1073
{ inner_query , _ } = normalize_select ( inner_query , true )
1090
1074
{ _ , 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
-
1102
1075
{ % { subquery | query: inner_query } , counter }
1103
1076
rescue
1104
1077
e -> raise Ecto.SubQueryError , query: query , exception: e
@@ -1267,11 +1240,7 @@ defmodule Ecto.Query.Planner do
1267
1240
end
1268
1241
end
1269
1242
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
1275
1244
% { assocs: assocs , preloads: preloads , select: select } = query
1276
1245
% { take: take , expr: expr } = select
1277
1246
{ tag , from_take } = Map . get ( take , 0 , { :any , [ ] } )
@@ -1319,6 +1288,10 @@ defmodule Ecto.Query.Planner do
1319
1288
{ put_in ( query . select . fields , fields ) , select }
1320
1289
end
1321
1290
1291
+ defp normalize_select ( query , _keep_literals? ) do
1292
+ { query , nil }
1293
+ end
1294
+
1322
1295
# Handling of source
1323
1296
1324
1297
defp collect_fields ( { :merge , _ , [ { :& , _ , [ 0 ] } , right ] } , fields , :none , query , take , keep_literals? ) do
@@ -1348,8 +1321,6 @@ defmodule Ecto.Query.Planner do
1348
1321
1349
1322
# Expression handling
1350
1323
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
1324
defp collect_fields ( { agg , _ , [ { { :. , dot_meta , [ { :& , _ , [ _ ] } , _ ] } , _ , [ ] } | _ ] } = expr ,
1354
1325
fields , from , _query , _take , _keep_literals? )
1355
1326
when agg in @ aggs do
@@ -1594,9 +1565,8 @@ defmodule Ecto.Query.Planner do
1594
1565
{ types , fields } = select_dump ( schema . __schema__ ( :query_fields ) , schema . __schema__ ( :dump ) , ix )
1595
1566
{ { :source , { source , schema } , prefix || query . prefix , types } , fields }
1596
1567
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 ) ) }
1600
1570
end
1601
1571
end
1602
1572
0 commit comments