diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74dd896eedd92..984844d72ffb5 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -907,10 +907,10 @@ def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions: update_typemap_for_default_timezone - stmt_key = prepare_statement(sql, binds) - type_casted_binds = type_casted_binds(binds) - with_raw_connection do |conn| + stmt_key = prepare_statement(sql, binds, conn) + type_casted_binds = type_casted_binds(binds) + log(sql, name, binds, type_casted_binds, stmt_key, async: async) do conn.exec_prepared(stmt_key, type_casted_binds) end @@ -960,22 +960,20 @@ def sql_key(sql) # Prepare the statement if it hasn't been prepared, return # the statement key. - def prepare_statement(sql, binds) - with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn| - sql_key = sql_key(sql) - unless @statements.key? sql_key - nextkey = @statements.next_key - begin - conn.prepare nextkey, sql - rescue => e - raise translate_exception_class(e, sql, binds) - end - # Clear the queue - conn.get_last_result - @statements[sql_key] = nextkey + def prepare_statement(sql, binds, conn) + sql_key = sql_key(sql) + unless @statements.key? sql_key + nextkey = @statements.next_key + begin + conn.prepare nextkey, sql + rescue => e + raise translate_exception_class(e, sql, binds) end - @statements[sql_key] + # Clear the queue + conn.get_last_result + @statements[sql_key] = nextkey end + @statements[sql_key] end # Connects to a PostgreSQL server and sets up the adapter depending on the diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index dc57d533db1c4..8479a87ea8e05 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -612,6 +612,20 @@ def teardown assert_predicate @connection, :active? end + test "querying after a failed query restores and succeeds" do + Post.first # Connection verified (and prepared statement pool populated if enabled) + + remote_disconnect @connection + + assert_raises(ActiveRecord::ConnectionFailed) do + Post.first # Connection no longer verified after failed query + end + + assert Post.first # Verifying the connection causes a reconnect and the query succeeds + + assert_predicate @connection, :active? + end + test "transaction restores after remote disconnection" do remote_disconnect @connection Post.transaction do