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