Skip to content

Commit d78dd23

Browse files
committed
Added support for .limited_update_conditions used in .update_all. Changed #add_limit_offset! to account for generating SQL fragments without "SELECT..." in it yet.
1 parent c91bf90 commit d78dd23

File tree

1 file changed

+20
-14
lines changed

1 file changed

+20
-14
lines changed

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,41 +287,37 @@ def rollback_db_transaction
287287
end
288288

289289
def add_limit_offset!(sql, options)
290+
# Validate and/or convert integers for :limit and :offets options.
290291
if options[:offset]
291292
raise ArgumentError, "offset should have a limit" unless options[:limit]
292-
unless options[:offset].kind_of?Integer
293+
unless options[:offset].kind_of?(Integer)
293294
if options[:offset] =~ /^\d+$/
294295
options[:offset] = options[:offset].to_i
295296
else
296297
raise ArgumentError, "offset should be an integer"
297298
end
298299
end
299300
end
300-
301-
if options[:limit] && !(options[:limit].kind_of?Integer)
302-
# is it just a string which should be an integer?
301+
if options[:limit] && !(options[:limit].kind_of?(Integer))
303302
if options[:limit] =~ /^\d+$/
304303
options[:limit] = options[:limit].to_i
305304
else
306305
raise ArgumentError, "limit should be an integer"
307306
end
308307
end
309-
308+
# The buisiness of adding limit/offset
310309
if options[:limit] and options[:offset]
311-
total_rows = raw_connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally")[0][:TotalRows].to_i
310+
total_rows = select_value("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally").to_i
312311
if (options[:limit] + options[:offset]) >= total_rows
313312
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
314313
end
315-
316314
# Wrap the SQL query in a bunch of outer SQL queries that emulate proper LIMIT,OFFSET support.
317315
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]}")
318316
sql << ") AS tmp1"
319-
320317
if options[:order]
321318
order = options[:order].split(',').map do |field|
322319
order_by_column, order_direction = field.split(" ")
323320
order_by_column = quote_column_name(order_by_column)
324-
325321
# Investigate the SQL query to figure out if the order_by_column has been renamed.
326322
if sql =~ /#{Regexp.escape(order_by_column)} AS (t\d_r\d\d?)/
327323
# Fx "[foo].[bar] AS t4_r2" was found in the SQL. Use the column alias (ie 't4_r2') for the subsequent orderings
@@ -333,19 +329,22 @@ def add_limit_offset!(sql, options)
333329
# name rather than the full identifier for the outer queries.
334330
order_by_column = order_by_column.split('.').last
335331
end
336-
337332
# Put the column name and eventual direction back together
338333
[order_by_column, order_direction].join(' ').strip
339334
end.join(', ')
340-
341335
sql << " ORDER BY #{change_order_direction(order)}) AS tmp2 ORDER BY #{order}"
342336
else
343337
sql << ") AS tmp2"
344338
end
345339
elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
346-
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
347-
"SELECT#{$1} TOP #{options[:limit]}"
348-
end unless options[:limit].nil? || options[:limit] < 1
340+
unless options[:limit].nil? || options[:limit] < 1
341+
if md = sql.match(/^(\s*SELECT)(\s+DISTINCT)?(.*)/im)
342+
sql.replace "#{md[1]}#{md[2]} TOP #{options[:limit]}#{md[3]}"
343+
else
344+
# Account for building SQL fragments without SELECT yet. See #update_all and #limited_update_conditions.
345+
sql.replace "TOP #{options[:limit]} #{sql}"
346+
end
347+
end
349348
end
350349
end
351350

@@ -365,6 +364,13 @@ def case_sensitive_equality_operator
365364
"COLLATE Latin1_General_CS_AS ="
366365
end
367366

367+
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
368+
match_data = where_sql.match(/(.*)WHERE/)
369+
limit = match_data[1]
370+
where_sql.sub!(limit,'')
371+
"WHERE #{quoted_primary_key} IN (SELECT #{limit} #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
372+
end
373+
368374
# SCHEMA STATEMENTS ========================================#
369375

370376
def native_database_types

0 commit comments

Comments
 (0)