@@ -2,21 +2,27 @@ module Arel
22
33 module Nodes
44
5+ # See the SelectManager#lock method on why this custom class is needed.
56 class LockWithSQLServer < Arel ::Nodes ::Unary
67 end
78
9+ # In versions of ActiveRecord prior to v3.0.3 limits and offset were always integers via
10+ # a #to_i. Somewhere between ActiveRecord and ARel this is not happening anymore nor are they
11+ # in agreement which should be responsible. Since we need to make sure that these are visited
12+ # correctly and that we can do math with them, these are here to cast to integers.
813 class Limit < Arel ::Nodes ::Unary
914 def initialize expr
1015 @expr = expr . to_i
1116 end
1217 end
13-
1418 class Offset < Arel ::Nodes ::Unary
1519 def initialize expr
1620 @expr = expr . to_i
1721 end
1822 end
1923
24+ # Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
25+ # collection of them. See SelectManager#order for more details.
2026 class Ordering < Arel ::Nodes ::Binary
2127 def hash
2228 expr . hash
@@ -35,32 +41,38 @@ class SelectManager < Arel::TreeManager
3541
3642 alias :lock_without_sqlserver :lock
3743
44+ # Getting real Ordering objects is very important for us. We need to be able to call #uniq on
45+ # a colleciton of them reliably as well as using their true object attributes to mutate them
46+ # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
47+ # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects.
3848 def order ( *exprs )
3949 @ast . orders . concat ( exprs . map { |x |
4050 case x
4151 when Arel ::Attributes ::Attribute
4252 c = engine . connection
4353 tn = x . relation . table_alias || x . relation . name
44- expr = Nodes ::SqlLiteral . new "#{ c . quote_table_name ( tn ) } .#{ c . quote_column_name ( x . name ) } "
45- Nodes ::Ordering . new expr
54+ expr = Arel :: Nodes ::SqlLiteral . new "#{ c . quote_table_name ( tn ) } .#{ c . quote_column_name ( x . name ) } "
55+ Arel :: Nodes ::Ordering . new expr
4656 when String
4757 x . split ( ',' ) . map do |s |
4858 expr , direction = s . split
49- expr = Nodes ::SqlLiteral . new ( expr )
59+ expr = Arel :: Nodes ::SqlLiteral . new ( expr )
5060 direction = direction =~ /desc/i ? :desc : :asc
51- Nodes ::Ordering . new expr , direction
61+ Arel :: Nodes ::Ordering . new expr , direction
5262 end
5363 else
54- expr = Nodes ::SqlLiteral . new x . to_s
55- Nodes ::Ordering . new expr
64+ expr = Arel :: Nodes ::SqlLiteral . new x . to_s
65+ Arel :: Nodes ::Ordering . new expr
5666 end
5767 } . flatten )
5868 self
5969 end
6070
71+ # A friendly over ride that allows us to put a special lock object that can have a default or pass
72+ # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method.
6173 def lock ( locking = true )
6274 if Arel ::Visitors ::SQLServer === @visitor
63- @ast . lock = Nodes ::LockWithSQLServer . new ( locking )
75+ @ast . lock = Arel :: Nodes ::LockWithSQLServer . new ( locking )
6476 self
6577 else
6678 lock_without_sqlserver ( locking )
@@ -121,17 +133,12 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false)
121133 if windowed
122134 projections = function_select_statement? ( o ) ? projections : projections . map { |x | projection_without_expression ( x ) }
123135 elsif eager_limiting_select_statement? ( o )
124- raise "visit_Arel_Nodes_SelectStatementWithOutOffset - eager_limiting_select_statement?"
125136 groups = projections . map { |x | projection_without_expression ( x ) }
126137 projections = projections . map { |x | projection_without_expression ( x ) }
127- # TODO: Let's alter objects vs strings and make new order objects
128138 orders = orders . map do |x |
129- Arel ::SqlLiteral . new ( x . split ( ',' ) . reject ( &:blank? ) . map do |c |
130- max = c =~ /desc\s */i
131- c = clause_without_expression ( c ) . sub ( /(asc|desc)/i , '' ) . strip
132- max ? "MAX(#{ c } )" : "MIN(#{ c } )"
133- end . join ( ', ' ) )
134- end
139+ expr = Arel ::Nodes ::SqlLiteral . new projection_without_expression ( x . expr )
140+ x . descending? ? Arel ::Nodes ::Max . new ( [ expr ] ) : Arel ::Nodes ::Min . new ( [ expr ] )
141+ end
135142 end
136143 [ ( "SELECT" if !windowed ) ,
137144 ( visit ( o . limit ) if o . limit && !windowed ) ,
@@ -266,8 +273,9 @@ def rowtable_orders(o)
266273 end . reverse . uniq . reverse
267274 end
268275
276+ # TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral.
269277 def projection_without_expression ( projection )
270- Arel ::SqlLiteral . new ( projection . split ( ',' ) . map do |x |
278+ Arel ::Nodes :: SqlLiteral . new ( projection . split ( ',' ) . map do |x |
271279 x . strip!
272280 x . sub! ( /^(COUNT|SUM|MAX|MIN|AVG)\s *(\( (.*)\) )?/ , '\3' )
273281 x . sub! ( /^DISTINCT\s */ , '' )
0 commit comments