Ensure StatementCache#execute never raises RangeError

Since 31ffbf8d, finder methods no longer raise `RangeError`. So
`StatementCache#execute` is the only place to raise the exception for
finder queries.

`StatementCache` is used for simple equality queries in the codebase.
This means that if `StatementCache#execute` raises `RangeError`, the
result could always be regarded as empty.
So `StatementCache#execute` just return nil in that range error case,
and treat that as empty in the caller side, then we can avoid catching
the exception in much places.
This commit is contained in:
Ryuta Kamizono 2017-07-21 12:26:09 +09:00
parent 31ffbf8d50
commit c196ca72a0
7 changed files with 19 additions and 19 deletions

@ -190,9 +190,7 @@ def find_target
end end
binds = AssociationScope.get_bind_values(owner, reflection.chain) binds = AssociationScope.get_bind_values(owner, reflection.chain)
sc.execute(binds, conn) do |record| sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
set_inverse_instance(record)
end
end end
# The scope for this association. # The scope for this association.

@ -37,8 +37,6 @@ def scope_for_create
def find_target def find_target
super.first super.first
rescue ::RangeError
nil
end end
def replace(record) def replace(record)

@ -169,15 +169,12 @@ def find(*ids) # :nodoc:
where(key => params.bind).limit(1) where(key => params.bind).limit(1)
} }
record = statement.execute([id], connection).first record = statement.execute([id], connection)&.first
unless record unless record
raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
name, primary_key, id) name, primary_key, id)
end end
record record
rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
name, primary_key)
end end
def find_by(*args) # :nodoc: def find_by(*args) # :nodoc:
@ -201,11 +198,9 @@ def find_by(*args) # :nodoc:
where(wheres).limit(1) where(wheres).limit(1)
} }
begin begin
statement.execute(hash.values, connection).first statement.execute(hash.values, connection)&.first
rescue TypeError rescue TypeError
raise ActiveRecord::StatementInvalid raise ActiveRecord::StatementInvalid
rescue ::RangeError
nil
end end
end end

@ -132,6 +132,8 @@ def execute(params, connection, &block)
sql = query_builder.sql_for bind_values, connection sql = query_builder.sql_for bind_values, connection
klass.find_by_sql(sql, bind_values, preparable: true, &block) klass.find_by_sql(sql, bind_values, preparable: true, &block)
rescue ::RangeError
nil
end end
def self.unsupported_value?(value) def self.unsupported_value?(value)

@ -1293,17 +1293,17 @@ def test_belongs_to_with_id_assigning
end end
def test_belongs_to_with_out_of_range_value_assigning def test_belongs_to_with_out_of_range_value_assigning
model = Class.new(Comment) do model = Class.new(Author) do
def self.name; "Temp"; end def self.name; "Temp"; end
validates :post, presence: true validates :author_address, presence: true
end end
comment = model.new author = model.new
comment.post_id = 9223372036854775808 # out of range in the bigint author.author_address_id = 9223372036854775808 # out of range in the bigint
assert_nil comment.post assert_nil author.author_address
assert_not_predicate comment, :valid? assert_not_predicate author, :valid?
assert_equal [{ error: :blank }], comment.errors.details[:post] assert_equal [{ error: :blank }], author.errors.details[:author_address]
end end
def test_polymorphic_with_custom_primary_key def test_polymorphic_with_custom_primary_key

@ -27,6 +27,7 @@
require "models/minivan" require "models/minivan"
require "models/speedometer" require "models/speedometer"
require "models/reference" require "models/reference"
require "models/job"
require "models/college" require "models/college"
require "models/student" require "models/student"
require "models/pirate" require "models/pirate"
@ -2926,6 +2927,11 @@ def test_create_children_could_be_rolled_back_by_after_save
end end
end end
def test_has_many_with_out_of_range_value
reference = Reference.create!(id: 2147483648) # out of range in the integer
assert_equal [], reference.ideal_jobs
end
private private
def force_signal37_to_load_all_clients_of_firm def force_signal37_to_load_all_clients_of_firm

@ -4,6 +4,7 @@ class Reference < ActiveRecord::Base
belongs_to :person belongs_to :person
belongs_to :job belongs_to :job
has_many :ideal_jobs, class_name: "Job", foreign_key: :ideal_reference_id
has_many :agents_posts_authors, through: :person has_many :agents_posts_authors, through: :person
class << self; attr_accessor :make_comments; end class << self; attr_accessor :make_comments; end