@@ -109,7 +109,7 @@ if Code.ensure_loaded?(Postgrex) do
109
109
end
110
110
111
111
alias Ecto.Query
112
- alias Ecto.Query . { BooleanExpr , JoinExpr , QueryExpr }
112
+ alias Ecto.Query . { BooleanExpr , JoinExpr , QueryExpr , PartitionByExpr }
113
113
114
114
def all ( query ) do
115
115
sources = create_names ( query )
@@ -121,12 +121,13 @@ if Code.ensure_loaded?(Postgrex) do
121
121
where = where ( query , sources )
122
122
group_by = group_by ( query , sources )
123
123
having = having ( query , sources )
124
+ window = window ( query , sources )
124
125
order_by = order_by ( query , order_by_distinct , sources )
125
126
limit = limit ( query , sources )
126
127
offset = offset ( query , sources )
127
128
lock = lock ( query . lock )
128
129
129
- [ select , from , join , where , group_by , having , order_by , limit , offset | lock ]
130
+ [ select , from , join , where , group_by , having , window , order_by , limit , offset | lock ]
130
131
end
131
132
132
133
def update_all ( % { from: % { source: source } } = query , prefix \\ nil ) do
@@ -380,6 +381,22 @@ if Code.ensure_loaded?(Postgrex) do
380
381
end ) ]
381
382
end
382
383
384
+ defp window ( % Query { windows: [ ] } , _sources ) , do: [ ]
385
+ defp window ( % Query { windows: windows } = query , sources ) do
386
+ [ " WINDOW " |
387
+ intersperse_map ( windows , ", " , fn
388
+ { name , definition } ->
389
+ [ Atom . to_string ( name ) , " AS " , partition_by ( definition , sources , query ) ] end ) ]
390
+ end
391
+
392
+ defp partition_by ( % PartitionByExpr { fields: fields , order_bys: order_bys } , sources , % Query { } = query ) do
393
+ partition_query = % Query { query | order_bys: order_bys }
394
+ [ "(PARTITION BY " ,
395
+ intersperse_map ( fields , ", " , & expr ( & 1 , sources , query ) ) ,
396
+ order_by ( partition_query , [ ] , sources ) ,
397
+ ?) ]
398
+ end
399
+
383
400
defp order_by ( % Query { order_bys: [ ] } , _distinct , _sources ) , do: [ ]
384
401
defp order_by ( % Query { order_bys: order_bys } = query , distinct , sources ) do
385
402
order_bys = Enum . flat_map ( order_bys , & & 1 . expr )
@@ -509,6 +526,25 @@ if Code.ensure_loaded?(Postgrex) do
509
526
[ aggregate , " FILTER (WHERE " , expr ( filter , sources , query ) , ?) ]
510
527
end
511
528
529
+ defp expr ( { :over , _ , [ agg , % PartitionByExpr { } = window ] } , sources , query ) do
530
+ aggregate = expr ( agg , sources , query )
531
+ [ aggregate , " OVER " , partition_by ( window , sources , query ) ]
532
+ end
533
+
534
+ defp expr ( { :over , _ , [ agg , nil ] } , sources , query ) do
535
+ aggregate = expr ( agg , sources , query )
536
+ [ aggregate , " OVER ()" ]
537
+ end
538
+
539
+ defp expr ( { :over , _ , [ agg , name ] } , sources , % Ecto.Query { windows: windows } = query ) when is_atom ( name ) do
540
+ if Keyword . has_key? ( windows , name ) do
541
+ aggregate = expr ( agg , sources , query )
542
+ [ aggregate , " OVER " , Atom . to_string ( name ) ]
543
+ else
544
+ error! ( query , "window #{ name } is undefined" )
545
+ end
546
+ end
547
+
512
548
defp expr ( { :{} , _ , elems } , sources , query ) do
513
549
[ ?( , intersperse_map ( elems , ?, , & expr ( & 1 , sources , query ) ) , ?) ]
514
550
end
0 commit comments