activerecord-sqlserver-adapter-vailsys 3.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ require 'active_record/connection_adapters/sqlserver_adapter'
@@ -0,0 +1,329 @@
1
+ require 'arel'
2
+
3
+ module Arel
4
+
5
+ module Nodes
6
+
7
+ # See the SelectManager#lock method on why this custom class is needed.
8
+ class LockWithSQLServer < Arel::Nodes::Unary
9
+ end
10
+
11
+ # Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
12
+ # collection of them. See SelectManager#order for more details.
13
+ class Ordering < Arel::Nodes::Binary
14
+ def hash
15
+ expr.hash
16
+ end
17
+ def ==(other)
18
+ self.class == other.class && self.expr == other.expr
19
+ end
20
+ def eql?(other)
21
+ self == other
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ class SelectManager < Arel::TreeManager
28
+
29
+ alias :lock_without_sqlserver :lock
30
+
31
+ # Getting real Ordering objects is very important for us. We need to be able to call #uniq on
32
+ # a colleciton of them reliably as well as using their true object attributes to mutate them
33
+ # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
34
+ # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects.
35
+ alias :order_without_sqlserver :order
36
+ def order(*exprs)
37
+ return order_without_sqlserver(*exprs) unless Arel::Visitors::SQLServer === @visitor
38
+ @ast.orders.concat(exprs.map{ |x|
39
+ case x
40
+ when Arel::Attributes::Attribute
41
+ table = Arel::Table.new(x.relation.table_alias || x.relation.name)
42
+ expr = table[x.name]
43
+ Arel::Nodes::Ordering.new expr
44
+ when Arel::Nodes::Ordering
45
+ x
46
+ when String
47
+ x.split(',').map do |s|
48
+ expr, direction = s.split
49
+ expr = Arel.sql(expr)
50
+ direction = direction =~ /desc/i ? :desc : :asc
51
+ Arel::Nodes::Ordering.new expr, direction
52
+ end
53
+ else
54
+ expr = Arel.sql(x.to_s)
55
+ Arel::Nodes::Ordering.new expr
56
+ end
57
+ }.flatten)
58
+ self
59
+ end
60
+
61
+ # A friendly over ride that allows us to put a special lock object that can have a default or pass
62
+ # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method.
63
+ def lock(locking=true)
64
+ if Arel::Visitors::SQLServer === @visitor
65
+ @ast.lock = Arel::Nodes::LockWithSQLServer.new(locking)
66
+ self
67
+ else
68
+ lock_without_sqlserver(locking)
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ module Visitors
75
+ class SQLServer < Arel::Visitors::ToSql
76
+
77
+ private
78
+
79
+ # SQLServer ToSql/Visitor (Overides)
80
+
81
+ def visit_Arel_Nodes_SelectStatement(o)
82
+ if complex_count_sql?(o)
83
+ visit_Arel_Nodes_SelectStatementForComplexCount(o)
84
+ elsif o.offset
85
+ visit_Arel_Nodes_SelectStatementWithOffset(o)
86
+ else
87
+ visit_Arel_Nodes_SelectStatementWithOutOffset(o)
88
+ end
89
+ end
90
+
91
+ def visit_Arel_Nodes_Offset(o)
92
+ "WHERE [__rnt].[__rn] > (#{visit o.expr})"
93
+ end
94
+
95
+ def visit_Arel_Nodes_Limit(o)
96
+ "TOP (#{visit o.expr})"
97
+ end
98
+
99
+ def visit_Arel_Nodes_Lock o
100
+ "WITH(HOLDLOCK, ROWLOCK)"
101
+ end
102
+
103
+ def visit_Arel_Nodes_LockWithSQLServer o
104
+ case o.expr
105
+ when TrueClass
106
+ "WITH(HOLDLOCK, ROWLOCK)"
107
+ when String
108
+ o.expr
109
+ else
110
+ ""
111
+ end
112
+ end
113
+
114
+
115
+ # SQLServer ToSql/Visitor (Additions)
116
+
117
+ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false)
118
+ find_and_fix_uncorrelated_joins_in_select_statement(o)
119
+ core = o.cores.first
120
+ projections = core.projections
121
+ groups = core.groups
122
+ orders = o.orders.uniq
123
+ if windowed
124
+ projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x) }
125
+ elsif eager_limiting_select_statement?(o)
126
+ groups = projections.map { |x| projection_without_expression(x) }
127
+ projections = projections.map { |x| projection_without_expression(x) }
128
+ orders = orders.map do |x|
129
+ expr = Arel.sql projection_without_expression(x.expr)
130
+ x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr])
131
+ end
132
+ end
133
+ [ ("SELECT" if !windowed),
134
+ (visit(o.limit) if o.limit && !windowed),
135
+ (projections.map{ |x| visit(x) }.join(', ')),
136
+ (source_with_lock_for_select_statement(o)),
137
+ ("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?),
138
+ ("GROUP BY #{groups.map { |x| visit x }.join ', ' }" unless groups.empty?),
139
+ (visit(core.having) if core.having),
140
+ ("ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}" if !orders.empty? && !windowed)
141
+ ].compact.join ' '
142
+ end
143
+
144
+ def visit_Arel_Nodes_SelectStatementWithOffset(o)
145
+ orders = rowtable_orders(o)
146
+ [ "SELECT",
147
+ (visit(o.limit) if o.limit && !single_distinct_select_statement?(o)),
148
+ (rowtable_projections(o).map{ |x| visit(x) }.join(', ')),
149
+ "FROM (",
150
+ "SELECT ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],",
151
+ visit_Arel_Nodes_SelectStatementWithOutOffset(o,true),
152
+ ") AS [__rnt]",
153
+ (visit(o.offset) if o.offset),
154
+ ].compact.join ' '
155
+ end
156
+
157
+ def visit_Arel_Nodes_SelectStatementForComplexCount(o)
158
+ core = o.cores.first
159
+ o.limit.expr = Arel.sql("#{o.limit.expr} + #{o.offset ? o.offset.expr : 0}") if o.limit
160
+ orders = rowtable_orders(o)
161
+ [ "SELECT COUNT([count]) AS [count_id]",
162
+ "FROM (",
163
+ "SELECT",
164
+ (visit(o.limit) if o.limit),
165
+ "ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],",
166
+ "1 AS [count]",
167
+ (source_with_lock_for_select_statement(o)),
168
+ ("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?),
169
+ ("GROUP BY #{core.groups.map { |x| visit x }.join ', ' }" unless core.groups.empty?),
170
+ (visit(core.having) if core.having),
171
+ ("ORDER BY #{o.orders.map{ |x| visit(x) }.join(', ')}" if !o.orders.empty?),
172
+ ") AS [__rnt]",
173
+ (visit(o.offset) if o.offset)
174
+ ].compact.join ' '
175
+ end
176
+
177
+
178
+ # SQLServer Helpers
179
+
180
+ def source_with_lock_for_select_statement(o)
181
+ # TODO: [ARel 2.2] Use #from/#source vs. #froms
182
+ core = o.cores.first
183
+ source = "FROM #{visit core.froms}" if core.froms
184
+ if source && o.lock
185
+ lock = visit o.lock
186
+ index = source.match(/FROM [\w\[\]\.]+/)[0].length
187
+ source.insert index, " #{lock}"
188
+ else
189
+ source
190
+ end
191
+ end
192
+
193
+ def table_from_select_statement(o)
194
+ core = o.cores.first
195
+ # TODO: [ARel 2.2] Use #from/#source vs. #froms
196
+ # if Arel::Table === core.from
197
+ # core.from
198
+ # elsif Arel::Nodes::SqlLiteral === core.from
199
+ # Arel::Table.new(core.from, @engine)
200
+ # elsif Arel::Nodes::JoinSource === core.source
201
+ # Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
202
+ # end
203
+ table_finder = lambda { |x|
204
+ case x
205
+ when Arel::Table
206
+ x
207
+ when Arel::Nodes::SqlLiteral
208
+ Arel::Table.new(x, @engine)
209
+ when Arel::Nodes::Join
210
+ table_finder.call(x.left)
211
+ end
212
+ }
213
+ table_finder.call(core.froms)
214
+ end
215
+
216
+ def single_distinct_select_statement?(o)
217
+ projections = o.cores.first.projections
218
+ p1 = projections.first
219
+ projections.size == 1 &&
220
+ ((p1.respond_to?(:distinct) && p1.distinct) ||
221
+ p1.respond_to?(:include?) && p1.include?('DISTINCT'))
222
+ end
223
+
224
+ def all_projections_aliased_in_select_statement?(o)
225
+ projections = o.cores.first.projections
226
+ projections.all? do |x|
227
+ x.split(',').all? { |y| y.include?(' AS ') }
228
+ end
229
+ end
230
+
231
+ def function_select_statement?(o)
232
+ core = o.cores.first
233
+ core.projections.any? { |x| Arel::Nodes::Function === x }
234
+ end
235
+
236
+ def eager_limiting_select_statement?(o)
237
+ core = o.cores.first
238
+ single_distinct_select_statement?(o) && (o.limit && !o.offset) && core.groups.empty?
239
+ end
240
+
241
+ def join_in_select_statement?(o)
242
+ core = o.cores.first
243
+ # TODO: [ARel 2.2] Use #from/#source vs. #froms
244
+ # core.source.right.any? { |x| Arel::Nodes::Join === x }
245
+ Arel::Nodes::Join === core.froms
246
+ end
247
+
248
+ def complex_count_sql?(o)
249
+ core = o.cores.first
250
+ core.projections.size == 1 &&
251
+ Arel::Nodes::Count === core.projections.first &&
252
+ o.limit &&
253
+ !join_in_select_statement?(o)
254
+ end
255
+
256
+ def find_and_fix_uncorrelated_joins_in_select_statement(o)
257
+ core = o.cores.first
258
+ # TODO: [ARel 2.2] Use #from/#source vs. #froms
259
+ # return if !join_in_select_statement?(o) || core.source.right.size != 2
260
+ # j1 = core.source.right.first
261
+ # j2 = core.source.right.second
262
+ # return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::StringJoin === j2
263
+ # j1_tn = j1.left.name
264
+ # j2_tn = j2.left.match(/JOIN \[(.*)\].*ON/).try(:[],1)
265
+ # return unless j1_tn == j2_tn
266
+ # crltd_tn = "#{j1_tn}_crltd"
267
+ # j1.left.table_alias = crltd_tn
268
+ # j1.right.expr.left.relation.table_alias = crltd_tn
269
+ return if !join_in_select_statement?(o) || !(Arel::Nodes::StringJoin === core.froms)
270
+ j1 = core.froms.left
271
+ j2 = core.froms.right
272
+ return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::SqlLiteral === j2 && j2.include?('JOIN ')
273
+ j1_tn = j1.right.name
274
+ j2_tn = j2.match(/JOIN \[(.*)\].*ON/).try(:[],1)
275
+ return unless j1_tn == j2_tn
276
+ on_index = j2.index(' ON ')
277
+ j2.insert on_index, " AS [#{j2_tn}_crltd]"
278
+ j2.sub! "[#{j2_tn}].", "[#{j2_tn}_crltd]."
279
+ end
280
+
281
+ def rowtable_projections(o)
282
+ core = o.cores.first
283
+ if single_distinct_select_statement?(o)
284
+ tn = table_from_select_statement(o).name
285
+ core.projections.map do |x|
286
+ x.dup.tap do |p|
287
+ p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit)}".strip if o.limit
288
+ p.gsub! /\[?#{tn}\]?\./, '[__rnt].'
289
+ p.strip!
290
+ end
291
+ end
292
+ elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o)
293
+ core.projections.map do |x|
294
+ Arel.sql x.split(',').map{ |y| y.split(' AS ').last.strip }.join(', ')
295
+ end
296
+ else
297
+ # TODO: [ARel 2.2] Use Arel.star
298
+ [Arel.sql('[__rnt].*')]
299
+ end
300
+ end
301
+
302
+ def rowtable_orders(o)
303
+ core = o.cores.first
304
+ if !o.orders.empty?
305
+ o.orders
306
+ else
307
+ t = table_from_select_statement(o)
308
+ c = t.primary_key || t.columns.first
309
+ [c.asc]
310
+ end.uniq
311
+ end
312
+
313
+ # TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral.
314
+ def projection_without_expression(projection)
315
+ Arel.sql(projection.split(',').map do |x|
316
+ x.strip!
317
+ x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/,'\3')
318
+ x.sub!(/^DISTINCT\s*/,'')
319
+ x.sub!(/TOP\s*\(\d+\)\s*/i,'')
320
+ x.strip
321
+ end.join(', '))
322
+ end
323
+
324
+ end
325
+ end
326
+
327
+ end
328
+
329
+ Arel::Visitors::VISITORS['sqlserver'] = Arel::Visitors::SQLServer
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-sqlserver-adapter-vailsys
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 3
7
+ - 0
8
+ - 20
9
+ version: 3.0.20
10
+ platform: ruby
11
+ authors:
12
+ - Brian Eng
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-11-02 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 3
29
+ - 0
30
+ - 3
31
+ version: 3.0.3
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: arel
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 2
43
+ - 0
44
+ - 7
45
+ version: 2.0.7
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ description: SQL Server 2005 and 2008 Adapter For ActiveRecord. Forked in order to disable unicode.
49
+ email: beng@vailsys.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - README.rdoc
56
+ files:
57
+ - CHANGELOG
58
+ - MIT-LICENSE
59
+ - README.rdoc
60
+ - lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb
61
+ - lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb
62
+ - lib/active_record/connection_adapters/sqlserver/database_limits.rb
63
+ - lib/active_record/connection_adapters/sqlserver/database_statements.rb
64
+ - lib/active_record/connection_adapters/sqlserver/errors.rb
65
+ - lib/active_record/connection_adapters/sqlserver/query_cache.rb
66
+ - lib/active_record/connection_adapters/sqlserver/quoting.rb
67
+ - lib/active_record/connection_adapters/sqlserver/schema_statements.rb
68
+ - lib/active_record/connection_adapters/sqlserver_adapter.rb
69
+ - lib/activerecord-sqlserver-adapter.rb
70
+ - lib/arel/visitors/sqlserver.rb
71
+ has_rdoc: true
72
+ homepage: https://github.com/beng336/activerecord-sqlserver-adapter-vailsys
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --main
78
+ - README.rdoc
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ requirements: []
96
+
97
+ rubyforge_project: activerecord-sqlserver-adapter
98
+ rubygems_version: 1.3.6
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: SQL Server 2005 and 2008 Adapter For ActiveRecord. Forked in order to disable unicode.
102
+ test_files: []
103
+