Skip to content

Commit dac40d7

Browse files
mgrunbergAidan Haran
and
Aidan Haran
authored
Rails 6.1: Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection. (#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 fef6c98 commit dac40d7

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
@@ -16,6 +16,7 @@
1616
- [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column
1717
- [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes
1818
- [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests
19+
- [#903](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/903) Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection
1920

2021
#### Changed
2122

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def execute_procedure(proc_name, *variables)
167167
log(sql, name) do
168168
case @connection_options[:mode]
169169
when :dblib
170-
result = @connection.execute(sql)
170+
result = ensure_established_connection! { dblib_execute(sql) }
171171
options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc }
172172
result.each(options) do |row|
173173
r = row.with_indifferent_access
@@ -357,13 +357,7 @@ def sp_executesql_sql(sql, types, params, name)
357357
def raw_connection_do(sql)
358358
case @connection_options[:mode]
359359
when :dblib
360-
result = @connection.execute(sql)
361-
362-
# TinyTDS returns false instead of raising an exception if connection fails.
363-
# Getting around this by raising an exception ourselves while this PR
364-
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
365-
raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
366-
360+
result = ensure_established_connection! { dblib_execute(sql) }
367361
result.do
368362
end
369363
ensure
@@ -428,7 +422,7 @@ def _raw_select(sql, options = {})
428422
def raw_connection_run(sql)
429423
case @connection_options[:mode]
430424
when :dblib
431-
@connection.execute(sql)
425+
ensure_established_connection! { dblib_execute(sql) }
432426
end
433427
end
434428

@@ -462,6 +456,21 @@ def finish_statement_handle(handle)
462456
end
463457
handle
464458
end
459+
460+
def dblib_execute(sql)
461+
@connection.execute(sql).tap do |result|
462+
# TinyTDS returns false instead of raising an exception if connection fails.
463+
# Getting around this by raising an exception ourselves while this PR
464+
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
465+
raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
466+
end
467+
end
468+
469+
def ensure_established_connection!
470+
raise TinyTds::Error, 'SQL Server client is not connected' unless @connection
471+
472+
yield
473+
end
465474
end
466475
end
467476
end

lib/active_record/connection_adapters/sqlserver_adapter.rb

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

444444
def translate_exception(e, message:, sql:, binds:)
445445
case message
446+
when /(SQL Server client is not connected)|(failed to execute statement)/i
447+
ConnectionNotEstablished.new(message)
446448
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
447449
RecordNotUnique.new(message, sql: sql, binds: binds)
448450
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)