@@ -3,6 +3,140 @@ module Visitors
33 class SQLServer < Arel ::Visitors ::ToSql
44
55
6+ private
7+
8+ # SQLServer ToSql/Visitor (Overides)
9+
10+ def visit_Arel_Nodes_SelectStatement ( o )
11+ if complex_count_sql? ( o )
12+ visit_Arel_Nodes_SelectStatementForComplexCount ( o )
13+ elsif o . offset
14+ visit_Arel_Nodes_SelectStatementWithOffset ( o )
15+ else
16+ visit_Arel_Nodes_SelectStatementWithOutOffset ( o )
17+ end
18+ end
19+
20+ def visit_Arel_Nodes_Offset ( o )
21+ "WHERE [__rnt].[__rn] > #{ visit o . expr } "
22+ end
23+
24+ def visit_Arel_Nodes_Limit ( o )
25+ "TOP (#{ visit o . expr } )"
26+ end
27+
28+
29+ # SQLServer ToSql/Visitor (Additions)
30+
31+ def visit_Arel_Nodes_SelectStatementWithOutOffset ( o , windowed = false )
32+ core = o . cores . first
33+ projections = core . projections
34+ if windowed && !function_select_statement? ( o )
35+ projections = projections . map { |x | projection_without_expression ( x ) }
36+ elsif eager_limiting_select? ( o )
37+
38+ end
39+ [ ( "SELECT" if !windowed ) ,
40+ ( visit ( o . limit ) if o . limit && !windowed ) ,
41+ ( projections . map { |x | visit ( x ) } . join ( ', ' ) ) ,
42+ ( "FROM #{ visit core . froms } " if core . froms ) ,
43+ ( visit ( o . lock ) if o . lock ) ,
44+ # (joins unless joins.blank?),
45+ ( "WHERE #{ core . wheres . map { |x | visit ( x ) } . join ' AND ' } " unless core . wheres . empty? ) ,
46+ ( "GROUP BY #{ core . groups . map { |x | visit x } . join ', ' } " unless core . groups . empty? ) ,
47+ ( visit ( core . having ) if core . having ) ,
48+ ( "ORDER BY #{ o . orders . map { |x | visit ( x ) } . join ( ', ' ) } " if !o . orders . empty? && !windowed )
49+ ] . compact . join ' '
50+ end
51+
52+ def visit_Arel_Nodes_SelectStatementWithOffset ( o )
53+ orders = rowtable_orders ( o )
54+ [ "SELECT" ,
55+ ( visit ( o . limit ) if o . limit && !single_distinct_select? ( o ) ) ,
56+ ( rowtable_projections ( o ) . map { |x | visit ( x ) } . join ( ', ' ) ) ,
57+ "FROM (" ,
58+ "SELECT ROW_NUMBER() OVER (ORDER BY #{ orders . map { |x | visit ( x ) } . uniq . join ( ', ' ) } ) AS [__rn]," ,
59+ visit_Arel_Nodes_SelectStatementWithOutOffset ( o , true ) ,
60+ ") AS [__rnt]" ,
61+ ( visit ( o . offset ) if o . offset ) ,
62+ ] . compact . join ' '
63+ end
64+
65+ def visit_Arel_Nodes_SelectStatementForComplexCount ( o )
66+
67+ end
68+
69+
70+ # SQLServer Helpers
71+
72+ def table_name_from_select_statement ( o )
73+ o . cores . first . source . left . name
74+ end
75+
76+ def single_distinct_select? ( o )
77+ projections = o . cores . first . projections
78+ projections . size == 1 && projections . first . include? ( 'DISTINCT' )
79+ end
80+
81+ def function_select_statement? ( o )
82+ core = o . cores . first
83+ core . projections . any? { |x | Arel ::Nodes ::Function === x }
84+ end
85+
86+ def eager_limiting_select? ( o )
87+ false
88+ # single_distinct_select?(o) && taken_only? && relation.group_clauses.blank?
89+ end
90+
91+ 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?
96+ end
97+
98+ def rowtable_projections ( o )
99+ core = o . cores . first
100+ if single_distinct_select? ( o )
101+ raise 'TODO: single_distinct_select'
102+ # ::Array.wrap(relation.select_clauses.first.dup.tap do |sc|
103+ # sc.sub! 'DISTINCT', "DISTINCT #{taken_clause if relation.taken.present?}".strip
104+ # sc.sub! table_name_from_select_clause(sc), '__rnt'
105+ # sc.strip!
106+ # end)
107+ elsif false # relation.join? && all_select_clauses_aliased?
108+ raise 'TODO: relation.join? && all_select_clauses_aliased?'
109+ # relation.select_clauses.map do |sc|
110+ # sc.split(',').map { |c| c.split(' AS ').last.strip }.join(', ')
111+ # end
112+ elsif function_select_statement? ( o )
113+ [ Arel . star ]
114+ else
115+ tn = table_name_from_select_statement ( o )
116+ core . projections . map { |x | x . gsub /\[ #{ tn } \] \. / , '[__rnt].' }
117+ end
118+ end
119+
120+ def rowtable_orders ( o )
121+ if !o . orders . empty?
122+ o . orders
123+ elsif false # TODO relation.join?
124+ # table_names_from_select_clauses.map { |tn| quote("#{tn}.#{pk_for_table(tn)}") }
125+ else
126+ tn = table_name_from_select_statement ( o )
127+ [ Arel ::Table . new ( tn , @engine ) . primary_key . asc ]
128+ end
129+ end
130+
131+ def projection_without_expression ( projection )
132+ projection . to_s . split ( ',' ) . map do |x |
133+ x . strip!
134+ x . sub! ( /^(COUNT|SUM|MAX|MIN|AVG)\s *(\( (.*)\) )?/ , '\3' )
135+ x . sub! ( /^DISTINCT\s */ , '' )
136+ x . sub! ( /TOP\s *\( \d +\) \s */i , '' )
137+ x . strip
138+ end . join ( ', ' )
139+ end
6140
7141 end
8142 end
0 commit comments