diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c346b5d93d..691c684d76 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -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 transaction. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 32244b1755..e1f29ea03a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -377,7 +377,7 @@ module ActiveRecord def sql_for_insert(sql, pk, id_value, sequence_name, binds) [sql, binds] end - + def last_inserted_id(result) row = result.rows.first row && row.first diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ba1cb05d2c..a222e2d653 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -97,6 +97,7 @@ module ActiveRecord @pool = pool @schema_cache = SchemaCache.new self @visitor = nil + @prepared_statements = false end def valid_type?(type) @@ -440,6 +441,10 @@ module ActiveRecord # override in derived class ActiveRecord::StatementInvalid.new(message, exception) end + + def without_prepared_statement?(binds) + @prepared_statements || binds.empty? + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 4b11ea795c..d502daf230 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -174,6 +174,7 @@ module ActiveRecord @quoted_column_names, @quoted_table_names = {}, {} if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) + @prepared_statements = true @visitor = Arel::Visitors::MySQL.new self else @visitor = unprepared_visitor diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 92796c996e..e790f731ea 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -229,7 +229,7 @@ module ActiveRecord alias exec_without_stmt exec_query - # Returns an ActiveRecord::Result instance. + # Returns an ActiveRecord::Result instance. def select(sql, name = nil, binds = []) exec_query(sql, name) end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 15b5452850..c65ec5559f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -283,7 +283,7 @@ module ActiveRecord # always be empty, since the bind variables will have been already # substituted and removed from binds by BindVisitor, so this will # effectively disable prepared statement usage completely. - if binds.empty? + if without_prepared_statement?(binds) result_set, affected_rows = exec_without_stmt(sql, name) else result_set, affected_rows = exec_stmt(sql, name, binds) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 527d13b9b0..86b96a77fb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -135,8 +135,8 @@ module ActiveRecord def exec_query(sql, name = 'SQL', binds = []) log(sql, name, binds) do - result = binds.empty? ? exec_no_cache(sql, binds) : - exec_cache(sql, binds) + result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) : + exec_cache(sql, binds) types = {} fields = result.fields diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 5b9453a579..13978fd113 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -531,6 +531,7 @@ module ActiveRecord super(connection, logger) if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) + @prepared_statements = true @visitor = Arel::Visitors::PostgreSQL.new self else @visitor = unprepared_visitor diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index df489a5b1f..136094dcc9 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -113,6 +113,7 @@ module ActiveRecord @config = config if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) + @prepared_statements = true @visitor = Arel::Visitors::SQLite.new self else @visitor = unprepared_visitor @@ -293,8 +294,8 @@ module ActiveRecord def exec_query(sql, name = nil, binds = []) log(sql, name, binds) do - # Don't cache statements without bind values - if binds.empty? + # Don't cache statements if they are not prepared + if without_prepared_statement?(binds) stmt = @connection.prepare(sql) cols = stmt.columns records = stmt.to_a diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index dc2de7b523..e3b441280b 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -565,6 +565,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal [topic_2, topic_1].sort, [topic_1, topic_2] 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 topic = Topic.create category = Category.create(:name => "comparison")