From cd9156d9a4bc98fff20af3f57512b786f9aa68da Mon Sep 17 00:00:00 2001 From: Michael Morris Date: Wed, 19 Oct 2016 14:40:01 +1100 Subject: [PATCH] Handle using transactions and resetting isolation level correctly when `READ_COMMITTED_SNAPSHOT` is set to `ON` --- .../sqlserver/transaction.rb | 10 +++++++- test/cases/transaction_test_sqlserver.rb | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 81a58e968..3d5052ab0 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -14,7 +14,15 @@ def sqlserver? def current_isolation_level return unless sqlserver? level = connection.user_options_isolation_level - level.blank? ? 'READ COMMITTED' : level.upcase + + # When READ_COMMITTED_SNAPSHOT is set to ON, + # user_options_isolation_level will be equal to 'read committed + # snapshot' which is not a valid isolation level + if level.blank? || level == 'read committed snapshot' + 'READ COMMITTED' + else + level.upcase + end end end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 6db739bfb..e16a63a53 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -52,6 +52,31 @@ class TransactionTestSQLServer < ActiveRecord::TestCase connection.user_options_isolation_level.must_match %r{read committed}i end + describe 'when READ_COMMITTED_SNAPSHOT is set' do + before do + connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON" + connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE" + end + + after do + connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF" + connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" + end + + it 'should use READ COMMITTED as an isolation level' do + connection.user_options_isolation_level.must_match "read committed snapshot" + + Ship.transaction(isolation: :serializable) do + Ship.create! name: 'Black Pearl' + end + + # We're actually testing that the isolation level was correctly reset to + # "READ COMMITTED", and that no exception was raised (it's reported back + # by SQL Server as "read committed snapshot"). + connection.user_options_isolation_level.must_match "read committed snapshot" + end + end + protected