Skip to content

Commit ed14c8d

Browse files
committed
Implement association limiting after a fashion from PostgreSQL using #add_order_by_for_association_limiting!
1 parent 3ab1753 commit ed14c8d

File tree

3 files changed

+29
-60
lines changed

3 files changed

+29
-60
lines changed

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -488,25 +488,12 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
488488
end
489489

490490
def add_order_by_for_association_limiting!(sql, options)
491+
# Disertation http://gist.github.com/24073
492+
# Information http://weblogs.sqlteam.com/jeffs/archive/2007/12/13/select-distinct-order-by-error.aspx
491493
return sql if options[:order].blank?
492-
# Strip any ASC or DESC from the orders for the select list
493-
# Build fields and order arrays
494-
# e.g.: options[:order] = 'table.[id], table2.[col2] desc'
495-
# fields = ['min(table.[id]) AS id', 'min(table2.[col2]) AS col2']
496-
# order = ['id', 'col2 desc']
497-
fields = []
498-
order = []
499-
options[:order].split(/\s*,\s*/).each do |str|
500-
# regex matches 'table_name.[column_name] asc' or 'column_name' ('table_name.', 'asc', '[', and ']' are optional)
501-
# $1 = 'table_name.[column_name]'
502-
# $2 = 'column_name'
503-
# $3 = ' asc'
504-
str =~ /((?:\w+\.)?\[?(\w+)\]?)(\s+asc|\s+desc)?/i
505-
fields << "MIN(#{$1}) AS #{$2}"
506-
order << "#{$2}#{$3}"
507-
end
508-
sql.gsub!(/(.+?) FROM/, "\\1, #{fields.join(',')} FROM")
509-
sql << " ORDER BY #{order.join(',')}"
494+
columns = sql.match(/SELECT\s+DISTINCT(.*)FROM/)[1].strip
495+
sql.sub!(/SELECT\s+DISTINCT/,'SELECT')
496+
sql << "GROUP BY #{columns} ORDER BY #{order_to_min_set(options[:order])}"
510497
end
511498

512499
def change_column_null(table_name, column_name, null, default = nil)
@@ -713,6 +700,19 @@ def get_table_name(sql)
713700
end
714701
end
715702

703+
def order_to_min_set(order)
704+
dir = ''
705+
orders = order.split(',').map(&:strip).reject(&:blank?)
706+
mins = orders.map do |o|
707+
if o =~ /\b(asc|desc)$/i
708+
dir = $1
709+
o = o.sub($1,'').strip
710+
end
711+
"MIN(#{o})"
712+
end
713+
"#{mins.join(', ')} #{dir}".strip
714+
end
715+
716716
def remove_sqlserver_columns_cache_for(table_name)
717717
cache_key = unqualify_table_name(table_name)
718718
@sqlserver_columns_cache[cache_key] = nil

lib/core_ext/active_record.rb

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,8 @@ def reset_column_information_with_sqlserver_columns_cache_support
1616
reset_column_information_without_sqlserver_columns_cache_support
1717
end
1818

19-
private
20-
21-
# Overwrite the ActiveRecord::Base method for SQL server.
22-
# GROUP BY is necessary for distinct orderings
23-
def construct_finder_sql_for_association_limiting(options, join_dependency)
24-
scope = scope(:find)
25-
is_distinct = !options[:joins].blank? || include_eager_conditions?(options) || include_eager_order?(options)
26-
27-
sql = "SELECT #{table_name}.#{connection.quote_column_name(primary_key)} FROM #{table_name} "
28-
29-
if is_distinct
30-
sql << join_dependency.join_associations.collect(&:association_join).join
31-
# merge_joins isn't defined in 2.1.1, but appears in edge
32-
if defined?(merge_joins)
33-
# The next line may fail with a nil error under 2.1.1 or other non-edge rails versions - Use this instead: add_joins!(sql, options, scope)
34-
add_joins!(sql, options[:joins], scope)
35-
else
36-
add_joins!(sql, options, scope)
37-
end
38-
end
39-
40-
add_conditions!(sql, options[:conditions], scope)
41-
add_group!(sql, options[:group], scope)
42-
43-
if options[:order] && is_distinct
44-
if sql =~ /GROUP\s+BY/i
45-
sql << ", #{table_name}.#{connection.quote_column_name(primary_key)}"
46-
else
47-
sql << " GROUP BY #{table_name}.#{connection.quote_column_name(primary_key)}"
48-
end #if sql =~ /GROUP BY/i
49-
50-
connection.add_order_by_for_association_limiting!(sql, options)
51-
else
52-
add_order!(sql, options[:order], scope)
53-
end
54-
55-
add_limit!(sql, options, scope)
56-
57-
return sanitize_sql(sql)
58-
end
59-
6019
end
6120

62-
6321
end
6422
end
6523
end

test/cases/adapter_test_sqlserver.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ def setup
117117

118118
end
119119

120+
should 'take all types of order options and convert them to MIN functions using #order_to_min_set' do
121+
single_order = 'comments.id'
122+
assert_equal 'MIN(comments.id)', @connection.send(:order_to_min_set,single_order)
123+
two_orders = 'comments.id, comments.post_id'
124+
assert_equal 'MIN(comments.id), MIN(comments.post_id)', @connection.send(:order_to_min_set,two_orders)
125+
single_order_with_desc = 'comments.id DESC'
126+
assert_equal 'MIN(comments.id) DESC', @connection.send(:order_to_min_set,single_order_with_desc)
127+
two_orders_with_asc = 'comments.id, comments.post_id ASC'
128+
assert_equal 'MIN(comments.id), MIN(comments.post_id) ASC', @connection.send(:order_to_min_set,two_orders_with_asc)
129+
end
130+
120131
context 'with different language' do
121132

122133
teardown do

0 commit comments

Comments
 (0)