1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Check if the SQL is not a prepared statement

When the adapter is with prepared statement disabled and the binds array
is not empty the connection adapter will try to set the binds values and
will fail. Now we are checking if the adapter has the prepared statement
disabled.

Fixes #12023
This commit is contained in:
Rafael Mendonça França 2013-08-27 23:41:29 -03:00
parent 3d60e9d550
commit f13b278568
10 changed files with 29 additions and 7 deletions

View file

@ -1,3 +1,9 @@
* Fix inserts with prepared statements disabled.
Fixes #12023.
*Rafael Mendonça França*
* Setting a has_one association on a new record no longer causes an empty * Setting a has_one association on a new record no longer causes an empty
transaction. transaction.

View file

@ -377,7 +377,7 @@ module ActiveRecord
def sql_for_insert(sql, pk, id_value, sequence_name, binds) def sql_for_insert(sql, pk, id_value, sequence_name, binds)
[sql, binds] [sql, binds]
end end
def last_inserted_id(result) def last_inserted_id(result)
row = result.rows.first row = result.rows.first
row && row.first row && row.first

View file

@ -97,6 +97,7 @@ module ActiveRecord
@pool = pool @pool = pool
@schema_cache = SchemaCache.new self @schema_cache = SchemaCache.new self
@visitor = nil @visitor = nil
@prepared_statements = false
end end
def valid_type?(type) def valid_type?(type)
@ -440,6 +441,10 @@ module ActiveRecord
# override in derived class # override in derived class
ActiveRecord::StatementInvalid.new(message, exception) ActiveRecord::StatementInvalid.new(message, exception)
end end
def without_prepared_statement?(binds)
@prepared_statements || binds.empty?
end
end end
end end
end end

View file

@ -174,6 +174,7 @@ module ActiveRecord
@quoted_column_names, @quoted_table_names = {}, {} @quoted_column_names, @quoted_table_names = {}, {}
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@visitor = Arel::Visitors::MySQL.new self @visitor = Arel::Visitors::MySQL.new self
else else
@visitor = unprepared_visitor @visitor = unprepared_visitor

View file

@ -229,7 +229,7 @@ module ActiveRecord
alias exec_without_stmt exec_query alias exec_without_stmt exec_query
# Returns an ActiveRecord::Result instance. # Returns an ActiveRecord::Result instance.
def select(sql, name = nil, binds = []) def select(sql, name = nil, binds = [])
exec_query(sql, name) exec_query(sql, name)
end end

View file

@ -283,7 +283,7 @@ module ActiveRecord
# always be empty, since the bind variables will have been already # always be empty, since the bind variables will have been already
# substituted and removed from binds by BindVisitor, so this will # substituted and removed from binds by BindVisitor, so this will
# effectively disable prepared statement usage completely. # effectively disable prepared statement usage completely.
if binds.empty? if without_prepared_statement?(binds)
result_set, affected_rows = exec_without_stmt(sql, name) result_set, affected_rows = exec_without_stmt(sql, name)
else else
result_set, affected_rows = exec_stmt(sql, name, binds) result_set, affected_rows = exec_stmt(sql, name, binds)

View file

@ -135,8 +135,8 @@ module ActiveRecord
def exec_query(sql, name = 'SQL', binds = []) def exec_query(sql, name = 'SQL', binds = [])
log(sql, name, binds) do log(sql, name, binds) do
result = binds.empty? ? exec_no_cache(sql, binds) : result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
exec_cache(sql, binds) exec_cache(sql, binds)
types = {} types = {}
fields = result.fields fields = result.fields

View file

@ -531,6 +531,7 @@ module ActiveRecord
super(connection, logger) super(connection, logger)
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@visitor = Arel::Visitors::PostgreSQL.new self @visitor = Arel::Visitors::PostgreSQL.new self
else else
@visitor = unprepared_visitor @visitor = unprepared_visitor

View file

@ -113,6 +113,7 @@ module ActiveRecord
@config = config @config = config
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@visitor = Arel::Visitors::SQLite.new self @visitor = Arel::Visitors::SQLite.new self
else else
@visitor = unprepared_visitor @visitor = unprepared_visitor
@ -293,8 +294,8 @@ module ActiveRecord
def exec_query(sql, name = nil, binds = []) def exec_query(sql, name = nil, binds = [])
log(sql, name, binds) do log(sql, name, binds) do
# Don't cache statements without bind values # Don't cache statements if they are not prepared
if binds.empty? if without_prepared_statement?(binds)
stmt = @connection.prepare(sql) stmt = @connection.prepare(sql)
cols = stmt.columns cols = stmt.columns
records = stmt.to_a records = stmt.to_a

View file

@ -565,6 +565,14 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal [topic_2, topic_1].sort, [topic_1, topic_2] assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
end end
def test_create_without_prepared_statement
topic = Topic.connection.unprepared_statement do
Topic.create(:title => 'foo')
end
assert_equal topic, Topic.find(topic.id)
end
def test_comparison_with_different_objects def test_comparison_with_different_objects
topic = Topic.create topic = Topic.create
category = Category.create(:name => "comparison") category = Category.create(:name => "comparison")