Skip to content

Commit 56c9112

Browse files
mgrunbergAidan Haran
authored and
Lavika
committed
Rails 6.1: Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection. (rails-sqlserver#903)
* raise ActiveRecord::ConnectionNotEstablished instead of ActiveRecord::StatementInvalid when call execute and connection is disconnected * add test of other calls to connection.execute that should fail * add changelog item * Refactor * fix typo * check connection active to prevent usage of dead/closed connection * raise TinyTds::Error and translate it. 'active?' method is rescuing just those * Revert "check connection active to prevent usage of dead/closed connection" This reverts commit 6861763. * check TinyTds FalseClass response in execute_procedure and raw_connection_run * raise ConnectionNotEstabilished when TinyTDS returns false Co-authored-by: Aidan Haran <aharan@fusioneer.com>
1 parent d892a78 commit 56c9112

File tree

4 files changed

+60
-9
lines changed

4 files changed

+60
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column
2121
- [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes
2222
- [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests
23+
- [#903](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/903) Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection
2324

2425
#### Changed
2526

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def execute_procedure(proc_name, *variables)
165165
log(sql, name) do
166166
case @connection_options[:mode]
167167
when :dblib
168-
result = @connection.execute(sql)
168+
result = ensure_established_connection! { dblib_execute(sql) }
169169
options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc }
170170
result.each(options) do |row|
171171
r = row.with_indifferent_access
@@ -375,13 +375,7 @@ def sp_executesql_sql(sql, types, params, name)
375375
def raw_connection_do(sql)
376376
case @connection_options[:mode]
377377
when :dblib
378-
result = @connection.execute(sql)
379-
380-
# TinyTDS returns false instead of raising an exception if connection fails.
381-
# Getting around this by raising an exception ourselves while this PR
382-
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
383-
raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
384-
378+
result = ensure_established_connection! { dblib_execute(sql) }
385379
result.do
386380
when :odbc
387381
@connection.do(sql)
@@ -448,7 +442,7 @@ def _raw_select(sql, options = {})
448442
def raw_connection_run(sql)
449443
case @connection_options[:mode]
450444
when :dblib
451-
@connection.execute(sql)
445+
ensure_established_connection! { dblib_execute(sql) }
452446
when :odbc
453447
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
454448
end
@@ -506,6 +500,21 @@ def finish_statement_handle(handle)
506500
end
507501
handle
508502
end
503+
504+
def dblib_execute(sql)
505+
@connection.execute(sql).tap do |result|
506+
# TinyTDS returns false instead of raising an exception if connection fails.
507+
# Getting around this by raising an exception ourselves while this PR
508+
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
509+
raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
510+
end
511+
end
512+
513+
def ensure_established_connection!
514+
raise TinyTds::Error, 'SQL Server client is not connected' unless @connection
515+
516+
yield
517+
end
509518
end
510519
end
511520
end

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ def initialize_type_map(m = type_map)
468468

469469
def translate_exception(e, message:, sql:, binds:)
470470
case message
471+
when /(SQL Server client is not connected)|(failed to execute statement)/i
472+
ConnectionNotEstablished.new(message)
471473
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
472474
RecordNotUnique.new(message, sql: sql, binds: binds)
473475
when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
require "cases/helper_sqlserver"
4+
5+
class TestDisconnectedAdapter < ActiveRecord::TestCase
6+
self.use_transactional_tests = false
7+
8+
def setup
9+
@connection = ActiveRecord::Base.connection
10+
end
11+
12+
teardown do
13+
return if in_memory_db?
14+
db_config = ActiveRecord::Base.connection_db_config
15+
ActiveRecord::Base.establish_connection(db_config)
16+
end
17+
18+
test "can't execute procedures while disconnected" do
19+
@connection.execute_procedure :sp_tables, "sst_datatypes"
20+
@connection.disconnect!
21+
assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
22+
@connection.execute_procedure :sp_tables, "sst_datatypes"
23+
end
24+
end
25+
26+
test "can't execute query while disconnected" do
27+
sql = "SELECT count(*) from products WHERE id IN(@0, @1)"
28+
binds = [
29+
ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new),
30+
ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new)
31+
]
32+
33+
@connection.exec_query sql, "TEST", binds
34+
@connection.disconnect!
35+
assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do
36+
@connection.exec_query sql, "TEST", binds
37+
end
38+
end
39+
end

0 commit comments

Comments
 (0)