Skip to content

Commit 7e6372a

Browse files
committed
AR's distinct compatible with 4.x (and 3.x) for Oracle, Derby and Postgres
1 parent ca645a2 commit 7e6372a

File tree

3 files changed

+47
-29
lines changed

3 files changed

+47
-29
lines changed

lib/arjdbc/derby/adapter.rb

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -342,20 +342,28 @@ def rename_column(table_name, column_name, new_column_name)
342342
# @note This is based on distinct method for the PostgreSQL Adapter.
343343
# @override
344344
def distinct(columns, order_by)
345-
return "DISTINCT #{columns}" if order_by.blank?
346-
347-
# construct a clean list of column names from the ORDER BY clause, removing
348-
# any asc/desc modifiers
349-
order_columns = [ order_by ].flatten!
350-
order_columns.map! do |o|
351-
o.split(',').collect! { |s| s.split.first }
352-
end # .flatten!
345+
"DISTINCT #{columns_for_distinct(columns, order_by)}"
346+
end
347+
348+
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
349+
def columns_for_distinct(columns, orders)
350+
return columns if orders.blank?
351+
352+
# construct a clean list of column names from the ORDER BY clause,
353+
# removing any ASC/DESC modifiers
354+
order_columns = [ orders ]; order_columns.flatten! # AR 3.x vs 4.x
355+
order_columns.map! do |column|
356+
column = column.to_sql unless column.is_a?(String) # handle AREL node
357+
column.split(',').collect! { |s| s.split.first }
358+
end.flatten!
353359
order_columns.reject!(&:blank?)
354-
order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s, i| "#{s} AS alias_#{i}" }
360+
order_columns = order_columns.zip (0...order_columns.size).to_a
361+
order_columns = order_columns.map { |s, i| "#{s} AS alias_#{i}" }
355362

356-
# return a DISTINCT clause that's distinct on the columns we want but includes
357-
# all the required columns for the ORDER BY to work properly
358-
"DISTINCT #{columns}, #{order_columns * ', '}"
363+
columns = [ columns ]; columns.flatten!
364+
columns.push( *order_columns ).join(', ')
365+
# return a DISTINCT clause that's distinct on the columns we want but
366+
# includes all the required columns for the ORDER BY to work properly
359367
end
360368

361369
# @override

lib/arjdbc/oracle/adapter.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,16 +295,27 @@ def remove_column(table_name, *column_names)
295295
# making every row the same.
296296
#
297297
# distinct("posts.id", "posts.created_at desc")
298+
#
299+
# @override
298300
def distinct(columns, order_by)
299-
return "DISTINCT #{columns}" if order_by.blank?
301+
"DISTINCT #{columns_for_distinct(columns, order_by)}"
302+
end
300303

304+
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
305+
def columns_for_distinct(columns, orders)
306+
return columns if orders.blank?
307+
if orders.is_a?(Array) # AR 3.x vs 4.x
308+
orders = orders.map { |column| column.is_a?(String) ? column : column.to_sql }
309+
else
310+
orders = extract_order_columns(orders)
311+
end
301312
# construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
302313
# FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
303-
order_columns = extract_order_columns(order_by).map do |c, i|
314+
order_columns = orders.map do |c, i|
304315
"FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
305316
end
306-
sql = "DISTINCT #{columns}, "
307-
sql << order_columns * ", "
317+
columns = [ columns ]; columns.flatten!
318+
columns.push( *order_columns ).join(', ')
308319
end
309320

310321
# ORDER BY clause for the passed order option.

lib/arjdbc/postgresql/adapter.rb

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -747,29 +747,28 @@ def multi_column_index_limit=(limit)
747747
@multi_column_index_limit = limit
748748
end
749749

750-
# Returns a SELECT DISTINCT clause for a given set of columns and a given
751-
# ORDER BY clause.
752-
#
750+
# @override
751+
def distinct(columns, orders)
752+
"DISTINCT #{columns_for_distinct(columns, orders)}"
753+
end
754+
753755
# PostgreSQL requires the ORDER BY columns in the select list for distinct
754756
# queries, and requires that the ORDER BY include the distinct column.
755-
#
756-
# distinct("posts.id", ["posts.created_at desc"])
757-
# # => "DISTINCT posts.id, posts.created_at AS alias_0"
758-
def distinct(columns, orders)
757+
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
758+
def columns_for_distinct(columns, orders)
759759
if orders.is_a?(String)
760760
orders = orders.split(','); orders.each(&:strip!)
761761
end
762762

763-
order_columns = orders.map do |column|
763+
order_columns = orders.reject(&:blank?).map! do |column|
764764
column = column.to_sql unless column.is_a?(String) # handle AREL node
765765
column.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') # remove ASC/DESC
766-
end.reject(&:blank?)
767-
768-
return "DISTINCT #{columns}" if order_columns.empty?
769-
766+
end
767+
order_columns.reject!(&:blank?)
770768
i = -1; order_columns.map! { |c| "#{c} AS alias_#{i += 1}" }
771769

772-
"DISTINCT #{columns}, #{order_columns.join(', ')}"
770+
columns = [ columns ]; columns.flatten!
771+
columns.push( *order_columns ).join(', ')
773772
end
774773

775774
# ORDER BY clause for the passed order option.

0 commit comments

Comments
 (0)