11module Arel
2+
3+ module Nodes
4+ class LockWithSQLServer < Arel ::Nodes ::Unary
5+ end
6+ end
7+
8+ class SelectManager < Arel ::TreeManager
9+
10+ alias :lock_without_sqlserver :lock
11+
12+ def lock ( locking = true )
13+ if Arel ::Visitors ::SQLServer === @visitor
14+ @ast . lock = Nodes ::LockWithSQLServer . new ( locking )
15+ self
16+ else
17+ lock_without_sqlserver ( locking )
18+ end
19+ end
20+
21+ end
22+
223 module Visitors
324 class SQLServer < Arel ::Visitors ::ToSql
425
5-
626 private
727
828 # SQLServer ToSql/Visitor (Overides)
@@ -25,6 +45,21 @@ def visit_Arel_Nodes_Limit(o)
2545 "TOP (#{ visit o . expr } )"
2646 end
2747
48+ def visit_Arel_Nodes_Lock o
49+ "WITH(HOLDLOCK, ROWLOCK)"
50+ end
51+
52+ def visit_Arel_Nodes_LockWithSQLServer o
53+ case o . expr
54+ when TrueClass
55+ "WITH(HOLDLOCK, ROWLOCK)"
56+ when String
57+ o . expr
58+ else
59+ ""
60+ end
61+ end
62+
2863
2964 # SQLServer ToSql/Visitor (Additions)
3065
@@ -34,14 +69,14 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false)
3469 if windowed && !function_select_statement? ( o )
3570 projections = projections . map { |x | projection_without_expression ( x ) }
3671 elsif eager_limiting_select? ( o )
37-
72+ # TODO visit_Arel_Nodes_SelectStatementWithOutOffset - eager_limiting_select
73+ raise 'visit_Arel_Nodes_SelectStatementWithOutOffset - eager_limiting_select'
3874 end
3975 [ ( "SELECT" if !windowed ) ,
4076 ( visit ( o . limit ) if o . limit && !windowed ) ,
4177 ( projections . map { |x | visit ( x ) } . join ( ', ' ) ) ,
42- ( "FROM #{ visit core . froms } " if core . froms ) ,
78+ visit ( core . source ) ,
4379 ( visit ( o . lock ) if o . lock ) ,
44- # (joins unless joins.blank?),
4580 ( "WHERE #{ core . wheres . map { |x | visit ( x ) } . join ' AND ' } " unless core . wheres . empty? ) ,
4681 ( "GROUP BY #{ core . groups . map { |x | visit x } . join ', ' } " unless core . groups . empty? ) ,
4782 ( visit ( core . having ) if core . having ) ,
@@ -58,12 +93,29 @@ def visit_Arel_Nodes_SelectStatementWithOffset(o)
5893 "SELECT ROW_NUMBER() OVER (ORDER BY #{ orders . map { |x | visit ( x ) } . uniq . join ( ', ' ) } ) AS [__rn]," ,
5994 visit_Arel_Nodes_SelectStatementWithOutOffset ( o , true ) ,
6095 ") AS [__rnt]" ,
61- ( visit ( o . offset ) if o . offset ) ,
96+ visit ( o . offset ) ,
6297 ] . compact . join ' '
6398 end
6499
65100 def visit_Arel_Nodes_SelectStatementForComplexCount ( o )
66-
101+ # joins = correlated_safe_joins
102+ core = o . cores . first
103+ orders = rowtable_orders ( o )
104+ [ "SELECT COUNT([count]) AS [count_id]" ,
105+ "FROM (" ,
106+ "SELECT" ,
107+ ( visit ( o . limit ) if o . limit ) ,
108+ "ROW_NUMBER() OVER (ORDER BY #{ orders . map { |x | visit ( x ) } . uniq . join ( ', ' ) } ) AS [__rn]," ,
109+ "1 AS [count]" ,
110+ visit ( core . source ) ,
111+ ( visit ( o . lock ) if o . lock ) ,
112+ ( "WHERE #{ core . wheres . map { |x | visit ( x ) } . join ' AND ' } " unless core . wheres . empty? ) ,
113+ ( "GROUP BY #{ core . groups . map { |x | visit x } . join ', ' } " unless core . groups . empty? ) ,
114+ ( visit ( core . having ) if core . having ) ,
115+ ( "ORDER BY #{ o . orders . map { |x | visit ( x ) } . uniq . join ( ', ' ) } " if !o . orders . empty? ) ,
116+ ") AS [__rnt]" ,
117+ ( visit ( o . offset ) )
118+ ] . compact . join ' '
67119 end
68120
69121
@@ -89,10 +141,14 @@ def eager_limiting_select?(o)
89141 end
90142
91143 def complex_count_sql? ( o )
92- false
93- # projections = relation.projections
94- # projections.first.is_a?(Arel::Count) && projections.size == 1 &&
95- # (relation.taken.present? || relation.wheres.present?) && relation.joins(self).blank?
144+ core = o . cores . first
145+ core . projections . size == 1 &&
146+ Arel ::Nodes ::Count === core . projections . first &&
147+ ( o . limit || !core . wheres . empty? ) &&
148+ true # TODO: This was - relation.joins(self).blank?
149+ # Consider visit(core.source)
150+ # Consider core.from
151+ # Consider core.froms
96152 end
97153
98154 def rowtable_projections ( o )
@@ -140,6 +196,7 @@ def projection_without_expression(projection)
140196
141197 end
142198 end
199+
143200end
144201
145202Arel ::Visitors ::VISITORS [ 'sqlserver' ] = Arel ::Visitors ::SQLServer
0 commit comments