Skip to content

Commit 841ed90

Browse files
authored
Merge pull request #769 from aidanharan/readonly-mode
Rails 6: Add ability to block writes to a database
2 parents 16c3949 + b9b5178 commit 841ed90

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,18 @@ module ActiveRecord
22
module ConnectionAdapters
33
module SQLServer
44
module DatabaseStatements
5+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback) # :nodoc:
6+
private_constant :READ_QUERY
7+
8+
def write_query?(sql) # :nodoc:
9+
!READ_QUERY.match?(sql)
10+
end
511

612
def execute(sql, name = nil)
13+
if preventing_writes? && write_query?(sql)
14+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
15+
end
16+
717
if id_insert_table_name = query_requires_identity_insert?(sql)
818
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
919
else
@@ -12,6 +22,10 @@ def execute(sql, name = nil)
1222
end
1323

1424
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
25+
if preventing_writes? && write_query?(sql)
26+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
27+
end
28+
1529
sp_executesql(sql, name, binds, prepare: prepare)
1630
end
1731

test/cases/adapter_test_sqlserver.rb

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
1313
let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
1414
let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" }
1515

16-
it 'has basic and non-senstive information in the adpaters inspect method' do
16+
it 'has basic and non-sensitive information in the adapters inspect method' do
1717
string = connection.inspect
1818
_(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
1919
_(string).must_match %r{version\: \d.\d}
@@ -429,5 +429,48 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
429429
end
430430
end
431431

432+
describe 'block writes to a database' do
433+
def setup
434+
@conn = ActiveRecord::Base.connection
435+
@connection_handler = ActiveRecord::Base.connection_handler
436+
end
437+
438+
def test_errors_when_an_insert_query_is_called_while_preventing_writes
439+
assert_raises(ActiveRecord::ReadOnlyError) do
440+
@connection_handler.while_preventing_writes do
441+
@conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
442+
end
443+
end
444+
end
445+
446+
def test_errors_when_an_update_query_is_called_while_preventing_writes
447+
@conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
448+
449+
assert_raises(ActiveRecord::ReadOnlyError) do
450+
@connection_handler.while_preventing_writes do
451+
@conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'")
452+
end
453+
end
454+
end
455+
456+
def test_errors_when_a_delete_query_is_called_while_preventing_writes
457+
@conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
458+
459+
assert_raises(ActiveRecord::ReadOnlyError) do
460+
@connection_handler.while_preventing_writes do
461+
@conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
462+
end
463+
end
464+
end
465+
466+
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
467+
@conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
468+
469+
@connection_handler.while_preventing_writes do
470+
assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
471+
end
472+
end
473+
end
474+
432475
end
433476

0 commit comments

Comments
 (0)