@@ -305,12 +305,14 @@ def add_limit_offset!(sql, options)
305305 raise ArgumentError , "limit should be an integer"
306306 end
307307 end
308- # The buisiness of adding limit/offset
308+ # The business of adding limit/offset
309309 if options [ :limit ] and options [ :offset ]
310310 total_rows = select_value ( "SELECT count(*) as TotalRows from (#{ sql . gsub ( /\b SELECT(\s +DISTINCT)?\b /i , "SELECT#{ $1} TOP 1000000000" ) } ) tally" ) . to_i
311311 if ( options [ :limit ] + options [ :offset ] ) >= total_rows
312312 options [ :limit ] = ( total_rows - options [ :offset ] >= 0 ) ? ( total_rows - options [ :offset ] ) : 0
313313 end
314+ # Make sure we do not need a special limit/offset for association limiting. http://gist.github.com/25118
315+ add_limit_offset_for_association_limiting! ( sql , options ) and return if sql_for_association_limiting? ( sql )
314316 # Wrap the SQL query in a bunch of outer SQL queries that emulate proper LIMIT,OFFSET support.
315317 sql . sub! ( /^\s *SELECT(\s +DISTINCT)?/i , "SELECT * FROM (SELECT TOP #{ options [ :limit ] } * FROM (SELECT#{ $1} TOP #{ options [ :limit ] + options [ :offset ] } " )
316318 sql << ") AS tmp1"
@@ -629,6 +631,21 @@ def handle_as_array(handle)
629631 array
630632 end
631633
634+ def add_limit_offset_for_association_limiting! ( sql , options )
635+ sql . replace %|
636+ SET NOCOUNT ON
637+ DECLARE @row_number TABLE (row int identity(1,1), id int)
638+ INSERT INTO @row_number (id)
639+ #{ sql }
640+ SET NOCOUNT OFF
641+ SELECT id FROM (
642+ SELECT TOP #{ options [ :limit ] } * FROM (
643+ SELECT TOP #{ options [ :limit ] + options [ :offset ] } * FROM @row_number ORDER BY row
644+ ) AS tmp1 ORDER BY row DESC
645+ ) AS tmp2 ORDER BY row
646+ | . gsub ( /[ \t \r \n ]+/ , ' ' )
647+ end
648+
632649 # SCHEMA STATEMENTS ========================================#
633650
634651 def remove_check_constraints ( table_name , column_name )
@@ -730,6 +747,13 @@ def order_to_min_set(order)
730747 end . join ( ', ' )
731748 end
732749
750+ def sql_for_association_limiting? ( sql )
751+ if md = sql . match ( /^\s *SELECT(.*)FROM.*GROUP BY.*ORDER BY.*/im )
752+ select_froms = md [ 1 ] . split ( ',' )
753+ select_froms . size == 1 && !select_froms . first . include? ( '*' )
754+ end
755+ end
756+
733757 def remove_sqlserver_columns_cache_for ( table_name )
734758 cache_key = unqualify_table_name ( table_name )
735759 @sqlserver_columns_cache [ cache_key ] = nil
0 commit comments