Skip to content

Commit 2b613fe

Browse files
committed
[Rails3] Move supporting limit/offset SQL manipulation to notes and defer a few cases.
1 parent 6c9aa73 commit 2b613fe

File tree

4 files changed

+46
-56
lines changed

4 files changed

+46
-56
lines changed

RAILS3_NOTES

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,47 @@ on a local branch of our remote tracking branch.
109109

110110

111111
* Review/remove our long winded add_limit_offset!
112-
Supporting:
113-
- add_lock!
114-
- add_limit_offset_for_association_limiting!
115-
- sql_for_association_limiting?
116-
- change_order_direction
112+
113+
def add_lock!(sql, options)
114+
# http://blog.sqlauthority.com/2007/04/27/sql-server-2005-locking-hints-and-examples/
115+
return unless options[:lock]
116+
lock_type = options[:lock] == true ? 'WITH(HOLDLOCK, ROWLOCK)' : options[:lock]
117+
sql.gsub! %r|LEFT OUTER JOIN\s+(.*?)\s+ON|im, "LEFT OUTER JOIN \\1 #{lock_type} ON"
118+
sql.gsub! %r{FROM\s([\w\[\]\.]+)}im, "FROM \\1 #{lock_type}"
119+
end
120+
121+
def add_limit_offset_for_association_limiting!(sql, options)
122+
sql.replace %|
123+
SET NOCOUNT ON
124+
DECLARE @row_number TABLE (row int identity(1,1), id int)
125+
INSERT INTO @row_number (id)
126+
#{sql}
127+
SET NOCOUNT OFF
128+
SELECT id FROM (
129+
SELECT TOP #{options[:limit]} * FROM (
130+
SELECT TOP #{options[:limit] + options[:offset]} * FROM @row_number ORDER BY row
131+
) AS tmp1 ORDER BY row DESC
132+
) AS tmp2 ORDER BY row
133+
|.gsub(/[ \t\r\n]+/,' ')
134+
end
135+
136+
def sql_for_association_limiting?(sql)
137+
if md = sql.match(/^\s*SELECT(.*)FROM.*GROUP BY.*ORDER BY.*/im)
138+
select_froms = md[1].split(',')
139+
select_froms.size == 1 && !select_froms.first.include?('*')
140+
end
141+
end
142+
143+
def change_order_direction(order)
144+
order.split(",").collect {|fragment|
145+
case fragment
146+
when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
147+
when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
148+
else String.new(fragment).split(',').join(' DESC,') + ' DESC'
149+
end
150+
}.join(",")
151+
end
152+
117153

118154
* Find out if this is needed anywhere.
119155

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -375,48 +375,6 @@ def finish_statement_handle(handle)
375375
handle
376376
end
377377

378-
# === SQLServer Specific (Misc, Doomed?) ======================== #
379-
380-
def add_lock!(sql, options)
381-
# http://blog.sqlauthority.com/2007/04/27/sql-server-2005-locking-hints-and-examples/
382-
return unless options[:lock]
383-
lock_type = options[:lock] == true ? 'WITH(HOLDLOCK, ROWLOCK)' : options[:lock]
384-
sql.gsub! %r|LEFT OUTER JOIN\s+(.*?)\s+ON|im, "LEFT OUTER JOIN \\1 #{lock_type} ON"
385-
sql.gsub! %r{FROM\s([\w\[\]\.]+)}im, "FROM \\1 #{lock_type}"
386-
end
387-
388-
def sql_for_association_limiting?(sql)
389-
if md = sql.match(/^\s*SELECT(.*)FROM.*GROUP BY.*ORDER BY.*/im)
390-
select_froms = md[1].split(',')
391-
select_froms.size == 1 && !select_froms.first.include?('*')
392-
end
393-
end
394-
395-
def add_limit_offset_for_association_limiting!(sql, options)
396-
sql.replace %|
397-
SET NOCOUNT ON
398-
DECLARE @row_number TABLE (row int identity(1,1), id int)
399-
INSERT INTO @row_number (id)
400-
#{sql}
401-
SET NOCOUNT OFF
402-
SELECT id FROM (
403-
SELECT TOP #{options[:limit]} * FROM (
404-
SELECT TOP #{options[:limit] + options[:offset]} * FROM @row_number ORDER BY row
405-
) AS tmp1 ORDER BY row DESC
406-
) AS tmp2 ORDER BY row
407-
|.gsub(/[ \t\r\n]+/,' ')
408-
end
409-
410-
def change_order_direction(order)
411-
order.split(",").collect {|fragment|
412-
case fragment
413-
when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
414-
when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
415-
else String.new(fragment).split(',').join(' DESC,') + ' DESC'
416-
end
417-
}.join(",")
418-
end
419-
420378
end
421379
end
422380
end

test/cases/adapter_test_sqlserver.rb

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,19 @@ def setup
140140

141141
context 'for #sql_for_association_limiting?' do
142142

143-
should 'return false for simple selects with no GROUP BY and ORDER BY' do
143+
should_eventually 'return false for simple selects with no GROUP BY and ORDER BY' do
144144
assert !sql_for_association_limiting?("SELECT * FROM [posts]")
145145
end
146146

147-
should 'return true to single SELECT, ideally a table/primarykey, that also has a GROUP BY and ORDER BY' do
147+
should_eventually 'return true to single SELECT, ideally a table/primarykey, that also has a GROUP BY and ORDER BY' do
148148
assert sql_for_association_limiting?("SELECT [posts].id FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
149149
end
150150

151-
should 'return false to single * wildcard SELECT that also has a GROUP BY and ORDER BY' do
151+
should_eventually 'return false to single * wildcard SELECT that also has a GROUP BY and ORDER BY' do
152152
assert !sql_for_association_limiting?("SELECT * FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
153153
end
154154

155-
should 'return false to multiple columns in the select even when GROUP BY and ORDER BY are present' do
155+
should_eventually 'return false to multiple columns in the select even when GROUP BY and ORDER BY are present' do
156156
sql = "SELECT [accounts].credit_limit, firm_id FROM...GROUP BY firm_id ORDER BY firm_id"
157157
assert !sql_for_association_limiting?(sql)
158158
end
@@ -690,10 +690,6 @@ def setup
690690

691691
private
692692

693-
def sql_for_association_limiting?(sql)
694-
@connection.send :sql_for_association_limiting?, sql
695-
end
696-
697693
def orders_and_dirs_set(order)
698694
@connection.send :orders_and_dirs_set, order
699695
end

test/cases/offset_and_limit_test_sqlserver.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def setup
7070
assert_equal(expected_sql, @connection.add_limit_offset!(@select_sql, options))
7171
end
7272

73-
should 'add locks to deepest sub select in limit offset sql that has a limited tally' do
73+
should_eventually 'add locks to deepest sub select in limit offset sql that has a limited tally' do
7474
options = { :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' }
7575
expected_sql = "SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books WITH (NOLOCK)) AS tmp1) AS tmp2"
7676
@connection.add_limit_offset! @select_sql, options

0 commit comments

Comments
 (0)