Skip to content

Commit

Permalink
Prevents coversion of string to order statement
Browse files Browse the repository at this point in the history
  • Loading branch information
danmcclain committed Sep 20, 2013
1 parent 72f1369 commit a442367
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
42 changes: 29 additions & 13 deletions lib/postgres_ext/active_record/relation/query_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def equality_to_function(function_name, opts)
end
end

[:with, :rank].each do |name|
[:with].each do |name|
class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}_values # def select_values
@values[:#{name}] || [] # @values[:select] || []
Expand All @@ -80,6 +80,19 @@ def #{name}_values=(values) # def select_values=(values)
CODE
end

[:rank].each do |name|
class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}_value=(value) # def readonly_value=(value)
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
@values[:#{name}] = value # @values[:readonly] = value
end # end
def #{name}_value # def readonly_value
@values[:#{name}] # @values[:readonly]
end # end
CODE
end

def with(*args)
check_if_method_has_arguments!('with', args)
spawn.with!(*args.compact.flatten)
Expand All @@ -94,8 +107,8 @@ def ranked(options = :order)
spawn.ranked! options
end

def ranked!(*args)
self.rank_values += args
def ranked!(value)
self.rank_value = value
self
end

Expand All @@ -104,7 +117,7 @@ def build_arel_with_extensions

build_with(arel, with_values)

build_rank(arel, rank_values)
build_rank(arel, rank_value) if rank_value

arel
end
Expand All @@ -129,24 +142,27 @@ def build_with(arel, withs)
arel.with with_statements unless with_statements.empty?
end

def build_rank(arel, ranks)
def build_rank(arel, rank_window_options)
unless arel.projections.count == 1 && Arel::Nodes::Count === arel.projections.first
rank_orders = ranks.uniq.reject(&:blank?).flat_map do |value|
case value
rank_window = case rank_window_options
when :order
arel.orders
when Symbol
table[value].asc
table[rank_window_options].asc
when Hash
value.map { |field, dir| table[field].send(dir) }
rank_window_options.map { |field, dir| table[field].send(dir) }
else
Arel::Nodes::SqlLiteral.new value
Arel::Nodes::SqlLiteral.new "(#{rank_window_options})"
end
end

unless rank_orders.blank?
unless rank_window.blank?
rank_node = Arel::Nodes::SqlLiteral.new 'rank()'
window = Arel::Nodes::Window.new.order(rank_orders)
window = Arel::Nodes::Window.new
if String === rank_window
window = window.frame rank_window
else
window = window.order(rank_window)
end
over_node = Arel::Nodes::Over.new rank_node, window

arel.project(over_node)
Expand Down
9 changes: 7 additions & 2 deletions spec/queries/window_functions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
end

it 'uses the rank value when a string passed to it' do
query = Person.ranked('lucky_number desc')
query.to_sql.should eq 'SELECT "people".*, rank() OVER (ORDER BY lucky_number desc) FROM "people"'
query = Person.ranked('ORDER BY lucky_number DESC')
query.to_sql.should eq 'SELECT "people".*, rank() OVER (ORDER BY lucky_number DESC) FROM "people"'
end

it 'combines the order and rank' do
Expand All @@ -44,6 +44,11 @@
it 'does not apply the rank when performing a count' do
query = Person.ranked(lucky_number: :desc).count
query.should eq 0

Person.create!

query = Person.ranked(lucky_number: :desc).count
query.should eq 1
end
end
end

0 comments on commit a442367

Please sign in to comment.