From f317cc8bc007978d7b135ddd1acdd7e3d1e582a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schu=CC=88rrer?= Date: Mon, 3 Mar 2014 17:44:55 +0100 Subject: Make exists? use bound values. When we build a query with an inline value that is a numeric (e.g. because it's out of range for an int4) PostgreSQL doesn't use an index on the column, since it's now comparing numerics and not int4s. This leads to a _very_ slow query. When we use bound parameters instead of inline values PostgreSQL raises numeric_value_out_of_range since no automatic coercion happens. --- .../lib/active_record/relation/finder_methods.rb | 7 ++++++- activerecord/test/cases/finder_test.rb | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 7099bdd285..1ba7fc47c0 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -292,7 +292,12 @@ module ActiveRecord when Array, Hash relation = relation.where(conditions) else - relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none + if conditions != :none + column = columns_hash[primary_key] + substitute = connection.substitute_at(column, bind_values.length) + relation = where(table[primary_key].eq(substitute)) + relation.bind_values += [[column, conditions]] + end end connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index b1eded6494..78c4e02434 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -58,15 +58,27 @@ class FinderTest < ActiveRecord::TestCase assert_equal false, Topic.exists?(45) assert_equal false, Topic.exists?(Topic.new) + assert_raise(NoMethodError) { Topic.exists?([1,2]) } + end + + def test_exists_fails_when_parameter_has_invalid_type + begin + assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int + flunk if defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter and Topic.connection.is_a? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter # PostgreSQL does raise here + rescue ActiveRecord::StatementInvalid + # PostgreSQL complains that it can't coerce a numeric that's bigger than int into int + rescue Exception + flunk + end + begin assert_equal false, Topic.exists?("foo") + flunk if defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter and Topic.connection.is_a? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter # PostgreSQL does raise here rescue ActiveRecord::StatementInvalid # PostgreSQL complains about string comparison with integer field rescue Exception flunk end - - assert_raise(NoMethodError) { Topic.exists?([1,2]) } end def test_exists_does_not_select_columns_without_alias -- cgit v1.2.3