activerecord-sqlserver-adapter 3.1.3 → 3.1.4
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.
- data/CHANGELOG +31 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +97 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +69 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +3 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +9 -4
- data/lib/active_record/connection_adapters/sqlserver/version.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +28 -6
- data/lib/arel/visitors/sqlserver.rb +19 -3
- metadata +6 -7
data/CHANGELOG
CHANGED
@@ -1,4 +1,35 @@
|
|
1
1
|
|
2
|
+
* 3.1.4 *
|
3
|
+
|
4
|
+
* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed.
|
5
|
+
Fixes #125. [Wüthrich Hannes @hwuethrich]
|
6
|
+
|
7
|
+
* New deadlock victim retry using the #retry_deadlock_victim config. [Joe Rafaniello]
|
8
|
+
|
9
|
+
* Renamed #with_auto_reconnect to #with_sqlserver_error_handling now that it handles both dropped
|
10
|
+
connections and deadlock victim errors. Fixes #150 [Joe Rafaniello]
|
11
|
+
|
12
|
+
* Add activity_stats method that mimics the SQL Server Activity Monitor. Fixes #146 [Joe Rafaniello]
|
13
|
+
|
14
|
+
* Add methods for sqlserver's #product_version, #product_level, #edition and include them in inspect.
|
15
|
+
Fixes #145 [Joe Rafaniello]
|
16
|
+
|
17
|
+
* Handle statements that cannot be retried on a new database connection by not reconnecting.
|
18
|
+
Fixes #147 [Joe Rafaniello]
|
19
|
+
|
20
|
+
* Added connection#spid for debugging. Fixes #144 [Joe Rafaniello]
|
21
|
+
|
22
|
+
* Add ENV['TEST_FILES'] to Rakefile for easy single case tests. [Joe Rafaniello]
|
23
|
+
|
24
|
+
* Pass ActiveRecord tests. Made windowed distinct pass all orders to groups.
|
25
|
+
- test_limited_eager_with_multiple_order_columns
|
26
|
+
- test_limited_eager_with_order
|
27
|
+
|
28
|
+
* Pass AR tests by moving DISTINCT to GROUP BY in windowed SQL.
|
29
|
+
- test_count_eager_with_has_many_and_limit_and_high_offset
|
30
|
+
- test_eager_with_has_many_and_limit_and_high_offset
|
31
|
+
|
32
|
+
|
2
33
|
* 3.1.3 *
|
3
34
|
|
4
35
|
* Distinguish between identity and primary key key columns during schema reflection. Allows us
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
module CoreExt
|
5
|
+
module DatabaseStatements
|
6
|
+
|
7
|
+
# This is a copy of the current (3.1.3) ActiveRecord's transaction method. We should propose
|
8
|
+
# a patch to the default transaction method to make it more callback for adapters that want to
|
9
|
+
# do deadlock retry logic. Because this is a copy, we really need to keep an eye out on this when
|
10
|
+
# upgradding the adapter.
|
11
|
+
def transaction_with_retry_deadlock_victim(options = {})
|
12
|
+
options.assert_valid_keys :requires_new, :joinable
|
13
|
+
|
14
|
+
last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
|
15
|
+
if options.has_key?(:joinable)
|
16
|
+
@transaction_joinable = options[:joinable]
|
17
|
+
else
|
18
|
+
@transaction_joinable = true
|
19
|
+
end
|
20
|
+
requires_new = options[:requires_new] || !last_transaction_joinable
|
21
|
+
|
22
|
+
transaction_open = false
|
23
|
+
@_current_transaction_records ||= []
|
24
|
+
|
25
|
+
begin
|
26
|
+
if block_given?
|
27
|
+
if requires_new || open_transactions == 0
|
28
|
+
if open_transactions == 0
|
29
|
+
begin_db_transaction
|
30
|
+
elsif requires_new
|
31
|
+
create_savepoint
|
32
|
+
end
|
33
|
+
increment_open_transactions
|
34
|
+
transaction_open = true
|
35
|
+
@_current_transaction_records.push([])
|
36
|
+
end
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
rescue Exception => database_transaction_rollback
|
40
|
+
if transaction_open && !outside_transaction?
|
41
|
+
transaction_open = false
|
42
|
+
decrement_open_transactions
|
43
|
+
# handle deadlock victim retries at the outermost transaction
|
44
|
+
if open_transactions == 0
|
45
|
+
if database_transaction_rollback.is_a?(::ActiveRecord::DeadlockVictim)
|
46
|
+
# SQL Server has already rolled back, so rollback activerecord's history
|
47
|
+
rollback_transaction_records(true)
|
48
|
+
retry
|
49
|
+
else
|
50
|
+
rollback_db_transaction
|
51
|
+
rollback_transaction_records(true)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
rollback_to_savepoint
|
55
|
+
rollback_transaction_records(false)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
raise unless database_transaction_rollback.is_a?(::ActiveRecord::Rollback)
|
59
|
+
end
|
60
|
+
ensure
|
61
|
+
@transaction_joinable = last_transaction_joinable
|
62
|
+
|
63
|
+
if outside_transaction?
|
64
|
+
@open_transactions = 0
|
65
|
+
elsif transaction_open
|
66
|
+
decrement_open_transactions
|
67
|
+
begin
|
68
|
+
if open_transactions == 0
|
69
|
+
commit_db_transaction
|
70
|
+
commit_transaction_records
|
71
|
+
else
|
72
|
+
release_savepoint
|
73
|
+
save_point_records = @_current_transaction_records.pop
|
74
|
+
unless save_point_records.blank?
|
75
|
+
@_current_transaction_records.push([]) if @_current_transaction_records.empty?
|
76
|
+
@_current_transaction_records.last.concat(save_point_records)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue Exception => database_transaction_rollback
|
80
|
+
if open_transactions == 0
|
81
|
+
rollback_db_transaction
|
82
|
+
rollback_transaction_records(true)
|
83
|
+
else
|
84
|
+
rollback_to_savepoint
|
85
|
+
rollback_transaction_records(false)
|
86
|
+
end
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -3,6 +3,8 @@ module ActiveRecord
|
|
3
3
|
module Sqlserver
|
4
4
|
module DatabaseStatements
|
5
5
|
|
6
|
+
include CoreExt::DatabaseStatements
|
7
|
+
|
6
8
|
def select_rows(sql, name = nil)
|
7
9
|
raw_select sql, name, [], :fetch => :rows
|
8
10
|
end
|
@@ -45,12 +47,20 @@ module ActiveRecord
|
|
45
47
|
true
|
46
48
|
end
|
47
49
|
|
50
|
+
def transaction(options = {})
|
51
|
+
if retry_deadlock_victim?
|
52
|
+
block_given? ? transaction_with_retry_deadlock_victim(options) { yield } : transaction_with_retry_deadlock_victim(options)
|
53
|
+
else
|
54
|
+
block_given? ? super(options) { yield } : super(options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
48
58
|
def begin_db_transaction
|
49
59
|
do_execute "BEGIN TRANSACTION"
|
50
60
|
end
|
51
61
|
|
52
62
|
def commit_db_transaction
|
53
|
-
do_execute "COMMIT TRANSACTION"
|
63
|
+
disable_auto_reconnect { do_execute "COMMIT TRANSACTION" }
|
54
64
|
end
|
55
65
|
|
56
66
|
def rollback_db_transaction
|
@@ -58,14 +68,14 @@ module ActiveRecord
|
|
58
68
|
end
|
59
69
|
|
60
70
|
def create_savepoint
|
61
|
-
do_execute "SAVE TRANSACTION #{current_savepoint_name}"
|
71
|
+
disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" }
|
62
72
|
end
|
63
73
|
|
64
74
|
def release_savepoint
|
65
75
|
end
|
66
76
|
|
67
77
|
def rollback_to_savepoint
|
68
|
-
do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}"
|
78
|
+
disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" }
|
69
79
|
end
|
70
80
|
|
71
81
|
def add_limit_offset!(sql, options)
|
@@ -185,6 +195,51 @@ module ActiveRecord
|
|
185
195
|
select_value "SELECT NEWSEQUENTIALID()"
|
186
196
|
end
|
187
197
|
|
198
|
+
def activity_stats
|
199
|
+
select_all %|
|
200
|
+
SELECT
|
201
|
+
[session_id] = s.session_id,
|
202
|
+
[user_process] = CONVERT(CHAR(1), s.is_user_process),
|
203
|
+
[login] = s.login_name,
|
204
|
+
[database] = ISNULL(db_name(r.database_id), N''),
|
205
|
+
[task_state] = ISNULL(t.task_state, N''),
|
206
|
+
[command] = ISNULL(r.command, N''),
|
207
|
+
[application] = ISNULL(s.program_name, N''),
|
208
|
+
[wait_time_ms] = ISNULL(w.wait_duration_ms, 0),
|
209
|
+
[wait_type] = ISNULL(w.wait_type, N''),
|
210
|
+
[wait_resource] = ISNULL(w.resource_description, N''),
|
211
|
+
[blocked_by] = ISNULL(CONVERT (varchar, w.blocking_session_id), ''),
|
212
|
+
[head_blocker] =
|
213
|
+
CASE
|
214
|
+
-- session has an active request, is blocked, but is blocking others
|
215
|
+
WHEN r2.session_id IS NOT NULL AND r.blocking_session_id = 0 THEN '1'
|
216
|
+
-- session is idle but has an open tran and is blocking others
|
217
|
+
WHEN r.session_id IS NULL THEN '1'
|
218
|
+
ELSE ''
|
219
|
+
END,
|
220
|
+
[total_cpu_ms] = s.cpu_time,
|
221
|
+
[total_physical_io_mb] = (s.reads + s.writes) * 8 / 1024,
|
222
|
+
[memory_use_kb] = s.memory_usage * 8192 / 1024,
|
223
|
+
[open_transactions] = ISNULL(r.open_transaction_count,0),
|
224
|
+
[login_time] = s.login_time,
|
225
|
+
[last_request_start_time] = s.last_request_start_time,
|
226
|
+
[host_name] = ISNULL(s.host_name, N''),
|
227
|
+
[net_address] = ISNULL(c.client_net_address, N''),
|
228
|
+
[execution_context_id] = ISNULL(t.exec_context_id, 0),
|
229
|
+
[request_id] = ISNULL(r.request_id, 0),
|
230
|
+
[workload_group] = N''
|
231
|
+
FROM sys.dm_exec_sessions s LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id)
|
232
|
+
LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id)
|
233
|
+
LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id AND r.request_id = t.request_id)
|
234
|
+
LEFT OUTER JOIN
|
235
|
+
(SELECT *, ROW_NUMBER() OVER (PARTITION BY waiting_task_address ORDER BY wait_duration_ms DESC) AS row_num
|
236
|
+
FROM sys.dm_os_waiting_tasks
|
237
|
+
) w ON (t.task_address = w.waiting_task_address) AND w.row_num = 1
|
238
|
+
LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id)
|
239
|
+
WHERE db_name(r.database_id) = '#{current_database}'
|
240
|
+
ORDER BY s.session_id|
|
241
|
+
end
|
242
|
+
|
188
243
|
# === SQLServer Specific (Rake/Test Helpers) ==================== #
|
189
244
|
|
190
245
|
def recreate_database
|
@@ -262,7 +317,7 @@ module ActiveRecord
|
|
262
317
|
def do_execute(sql, name = nil)
|
263
318
|
name ||= 'EXECUTE'
|
264
319
|
log(sql, name) do
|
265
|
-
|
320
|
+
with_sqlserver_error_handling { raw_connection_do(sql) }
|
266
321
|
end
|
267
322
|
end
|
268
323
|
|
@@ -307,18 +362,20 @@ module ActiveRecord
|
|
307
362
|
# === SQLServer Specific (Selecting) ============================ #
|
308
363
|
|
309
364
|
def raw_select(sql, name=nil, binds=[], options={})
|
310
|
-
log(sql,name,binds)
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
365
|
+
log(sql,name,binds) { _raw_select(sql, options) }
|
366
|
+
end
|
367
|
+
|
368
|
+
def _raw_select(sql, options={})
|
369
|
+
begin
|
370
|
+
handle = raw_connection_run(sql)
|
371
|
+
handle_to_names_and_values(handle, options)
|
372
|
+
ensure
|
373
|
+
finish_statement_handle(handle)
|
317
374
|
end
|
318
375
|
end
|
319
376
|
|
320
377
|
def raw_connection_run(sql)
|
321
|
-
|
378
|
+
with_sqlserver_error_handling do
|
322
379
|
case @connection_options[:mode]
|
323
380
|
when :dblib
|
324
381
|
@connection.execute(sql)
|
@@ -193,7 +193,7 @@ module ActiveRecord
|
|
193
193
|
ELSE NULL
|
194
194
|
END AS [is_nullable],
|
195
195
|
CASE
|
196
|
-
WHEN
|
196
|
+
WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1
|
197
197
|
ELSE NULL
|
198
198
|
END AS [is_primary],
|
199
199
|
CASE
|
@@ -201,12 +201,17 @@ module ActiveRecord
|
|
201
201
|
ELSE NULL
|
202
202
|
END AS [is_identity]
|
203
203
|
FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns
|
204
|
-
LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
205
|
-
|
204
|
+
LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
205
|
+
ON TC.TABLE_NAME = columns.TABLE_NAME
|
206
|
+
AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
|
207
|
+
LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
|
208
|
+
ON KCU.COLUMN_NAME = columns.COLUMN_NAME
|
209
|
+
AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
|
210
|
+
AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
|
211
|
+
AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
|
206
212
|
WHERE columns.TABLE_NAME = @0
|
207
213
|
AND columns.TABLE_SCHEMA = #{table_schema.blank? ? "schema_name()" : "@1"}
|
208
214
|
ORDER BY columns.ordinal_position
|
209
|
-
|
210
215
|
}.gsub(/[ \t\r\n]+/,' ')
|
211
216
|
binds = [['table_name', table_name]]
|
212
217
|
binds << ['table_schema',table_schema] unless table_schema.blank?
|
@@ -2,6 +2,7 @@ require 'arel/visitors/sqlserver'
|
|
2
2
|
require 'active_record'
|
3
3
|
require 'active_record/connection_adapters/abstract_adapter'
|
4
4
|
require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
|
5
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/database_statements'
|
5
6
|
require 'active_record/connection_adapters/sqlserver/database_limits'
|
6
7
|
require 'active_record/connection_adapters/sqlserver/database_statements'
|
7
8
|
require 'active_record/connection_adapters/sqlserver/errors'
|
@@ -176,10 +177,10 @@ module ActiveRecord
|
|
176
177
|
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
|
177
178
|
SUPPORTED_VERSIONS = [2005,2008,2010,2011].freeze
|
178
179
|
|
179
|
-
attr_reader :database_version, :database_year
|
180
|
+
attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition
|
180
181
|
|
181
182
|
cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
|
182
|
-
:log_info_schema_queries, :enable_default_unicode_types, :auto_connect,
|
183
|
+
:log_info_schema_queries, :enable_default_unicode_types, :auto_connect, :retry_deadlock_victim,
|
183
184
|
:cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration
|
184
185
|
|
185
186
|
self.enable_default_unicode_types = true
|
@@ -208,6 +209,9 @@ module ActiveRecord
|
|
208
209
|
rescue
|
209
210
|
0
|
210
211
|
end
|
212
|
+
@product_level = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))") }
|
213
|
+
@product_version = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))") }
|
214
|
+
@edition = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))") }
|
211
215
|
initialize_dateformatter
|
212
216
|
initialize_sqlserver_caches
|
213
217
|
use_database
|
@@ -269,6 +273,7 @@ module ActiveRecord
|
|
269
273
|
end
|
270
274
|
|
271
275
|
def disconnect!
|
276
|
+
@spid = nil
|
272
277
|
case @connection_options[:mode]
|
273
278
|
when :dblib
|
274
279
|
@connection.close rescue nil
|
@@ -323,7 +328,7 @@ module ActiveRecord
|
|
323
328
|
end
|
324
329
|
|
325
330
|
def inspect
|
326
|
-
"#<#{self.class} version: #{version}, year: #{@database_year}, connection_options: #{@connection_options.inspect}>"
|
331
|
+
"#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>"
|
327
332
|
end
|
328
333
|
|
329
334
|
def auto_connect
|
@@ -334,6 +339,11 @@ module ActiveRecord
|
|
334
339
|
@@auto_connect_duration ||= 10
|
335
340
|
end
|
336
341
|
|
342
|
+
def retry_deadlock_victim
|
343
|
+
@@retry_deadlock_victim.is_a?(FalseClass) ? false : true
|
344
|
+
end
|
345
|
+
alias :retry_deadlock_victim? :retry_deadlock_victim
|
346
|
+
|
337
347
|
def native_string_database_type
|
338
348
|
@@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
|
339
349
|
end
|
@@ -358,7 +368,6 @@ module ActiveRecord
|
|
358
368
|
@@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS'
|
359
369
|
end
|
360
370
|
|
361
|
-
|
362
371
|
protected
|
363
372
|
|
364
373
|
# === Abstract Adapter (Misc Support) =========================== #
|
@@ -369,6 +378,8 @@ module ActiveRecord
|
|
369
378
|
RecordNotUnique.new(message,e)
|
370
379
|
when /conflicted with the foreign key constraint/i
|
371
380
|
InvalidForeignKey.new(message,e)
|
381
|
+
when /has been chosen as the deadlock victim/i
|
382
|
+
DeadlockVictim.new(message,e)
|
372
383
|
when *lost_connection_messages
|
373
384
|
LostConnection.new(message,e)
|
374
385
|
else
|
@@ -431,6 +442,7 @@ module ActiveRecord
|
|
431
442
|
end
|
432
443
|
end
|
433
444
|
end
|
445
|
+
@spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first
|
434
446
|
configure_connection
|
435
447
|
rescue
|
436
448
|
raise unless @auto_connecting
|
@@ -468,15 +480,25 @@ module ActiveRecord
|
|
468
480
|
end if block_given?
|
469
481
|
end
|
470
482
|
|
471
|
-
def
|
483
|
+
def with_sqlserver_error_handling
|
472
484
|
begin
|
473
485
|
yield
|
474
486
|
rescue Exception => e
|
475
|
-
|
487
|
+
case translate_exception(e,e.message)
|
488
|
+
when LostConnection; retry if auto_reconnected?
|
489
|
+
when DeadlockVictim; retry if retry_deadlock_victim? && open_transactions == 0
|
490
|
+
end
|
476
491
|
raise
|
477
492
|
end
|
478
493
|
end
|
479
494
|
|
495
|
+
def disable_auto_reconnect
|
496
|
+
old_auto_connect, self.class.auto_connect = self.class.auto_connect, false
|
497
|
+
yield
|
498
|
+
ensure
|
499
|
+
self.class.auto_connect = old_auto_connect
|
500
|
+
end
|
501
|
+
|
480
502
|
def auto_reconnected?
|
481
503
|
return false unless auto_connect
|
482
504
|
@auto_connecting = true
|
@@ -138,9 +138,11 @@ module Arel
|
|
138
138
|
orders = o.orders.uniq
|
139
139
|
if windowed
|
140
140
|
projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x) }
|
141
|
+
groups = projections.map { |x| projection_without_expression(x) } if windowed_single_distinct_select_statement?(o) && groups.empty?
|
142
|
+
groups += orders.map { |x| Arel.sql(x.expr) } if windowed_single_distinct_select_statement?(o)
|
141
143
|
elsif eager_limiting_select_statement?(o)
|
142
|
-
groups = projections.map { |x| projection_without_expression(x) }
|
143
144
|
projections = projections.map { |x| projection_without_expression(x) }
|
145
|
+
groups = projections.map { |x| projection_without_expression(x) }
|
144
146
|
orders = orders.map do |x|
|
145
147
|
expr = Arel.sql projection_without_expression(x.expr)
|
146
148
|
x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr])
|
@@ -162,7 +164,7 @@ module Arel
|
|
162
164
|
def visit_Arel_Nodes_SelectStatementWithOffset(o)
|
163
165
|
orders = rowtable_orders(o)
|
164
166
|
[ "SELECT",
|
165
|
-
(visit(o.limit) if o.limit && !
|
167
|
+
(visit(o.limit) if o.limit && !windowed_single_distinct_select_statement?(o)),
|
166
168
|
(rowtable_projections(o).map{ |x| visit(x) }.join(', ')),
|
167
169
|
"FROM (",
|
168
170
|
"SELECT ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],",
|
@@ -238,6 +240,10 @@ module Arel
|
|
238
240
|
p1.respond_to?(:include?) && p1.include?('DISTINCT'))
|
239
241
|
end
|
240
242
|
|
243
|
+
def windowed_single_distinct_select_statement?(o)
|
244
|
+
o.limit && o.offset && single_distinct_select_statement?(o)
|
245
|
+
end
|
246
|
+
|
241
247
|
def single_distinct_select_everything_statement?(o)
|
242
248
|
single_distinct_select_statement?(o) && visit(o.cores.first.projections.first).ends_with?(".*")
|
243
249
|
end
|
@@ -316,7 +322,17 @@ module Arel
|
|
316
322
|
|
317
323
|
def rowtable_projections(o)
|
318
324
|
core = o.cores.first
|
319
|
-
if
|
325
|
+
if windowed_single_distinct_select_statement?(o) && core.groups.blank?
|
326
|
+
tn = table_from_select_statement(o).name
|
327
|
+
core.projections.map do |x|
|
328
|
+
x.dup.tap do |p|
|
329
|
+
p.sub! 'DISTINCT', ''
|
330
|
+
p.insert 0, visit(o.limit) if o.limit
|
331
|
+
p.gsub! /\[?#{tn}\]?\./, '[__rnt].'
|
332
|
+
p.strip!
|
333
|
+
end
|
334
|
+
end
|
335
|
+
elsif single_distinct_select_statement?(o)
|
320
336
|
tn = table_from_select_statement(o).name
|
321
337
|
core.projections.map do |x|
|
322
338
|
x.dup.tap do |p|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-sqlserver-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 3.1.
|
9
|
+
- 4
|
10
|
+
version: 3.1.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ken Collins
|
@@ -19,8 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2011-
|
23
|
-
default_executable:
|
22
|
+
date: 2011-11-30 00:00:00 Z
|
24
23
|
dependencies:
|
25
24
|
- !ruby/object:Gem::Dependency
|
26
25
|
name: activerecord
|
@@ -50,6 +49,7 @@ files:
|
|
50
49
|
- CHANGELOG
|
51
50
|
- MIT-LICENSE
|
52
51
|
- lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb
|
52
|
+
- lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb
|
53
53
|
- lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb
|
54
54
|
- lib/active_record/connection_adapters/sqlserver/database_limits.rb
|
55
55
|
- lib/active_record/connection_adapters/sqlserver/database_statements.rb
|
@@ -60,7 +60,6 @@ files:
|
|
60
60
|
- lib/active_record/connection_adapters/sqlserver_adapter.rb
|
61
61
|
- lib/activerecord-sqlserver-adapter.rb
|
62
62
|
- lib/arel/visitors/sqlserver.rb
|
63
|
-
has_rdoc: true
|
64
63
|
homepage: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
65
64
|
licenses: []
|
66
65
|
|
@@ -90,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
89
|
requirements: []
|
91
90
|
|
92
91
|
rubyforge_project: activerecord-sqlserver-adapter
|
93
|
-
rubygems_version: 1.
|
92
|
+
rubygems_version: 1.8.8
|
94
93
|
signing_key:
|
95
94
|
specification_version: 3
|
96
95
|
summary: SQL Server 2005 and 2008 Adapter For ActiveRecord.
|