Skip to content

Commit ad93a6b

Browse files
authored
Merge pull request #426 from masamitsu-murase/support_default_transaction_mode
Support default transaction mode.
2 parents 0c59601 + f140292 commit ad93a6b

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Vendored sqlite is update to [v3.44.2](https://sqlite.org/releaselog/3_44_2.html). @flavorjones
88

9+
### Added
10+
11+
- `Database.new` now accepts a `:default_transaction_mode` option (defaulting to `:deferred`), and `Database#transaction` no longer requires a transaction mode to be specified. This should allow higher-level adapters to more easily choose a transaction mode for a database connection. [#426] @masamitsu-murase
12+
913

1014
## 1.6.8 / 2023-11-01
1115

lib/sqlite3/database.rb

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,23 @@ def quote( string )
7171

7272
# call-seq: SQLite3::Database.new(file, options = {})
7373
#
74-
# Create a new Database object that opens the given file. If utf16
75-
# is +true+, the filename is interpreted as a UTF-16 encoded string.
74+
# Create a new Database object that opens the given file.
75+
#
76+
# Supported permissions +options+:
77+
# - the default mode is <tt>READWRITE | CREATE</tt>
78+
# - +:readonly+: boolean (default false), true to set the mode to +READONLY+
79+
# - +:readwrite+: boolean (default false), true to set the mode to +READWRITE+
80+
# - +:flags+: set the mode to a combination of SQLite3::Constants::Open flags.
81+
#
82+
# Supported encoding +options+:
83+
# - +:utf16+: boolean (default false), is the filename's encoding UTF-16 (only needed if the filename encoding is not UTF_16LE or BE)
84+
#
85+
# Other supported +options+:
86+
# - +:strict+: boolean (default false), disallow the use of double-quoted string literals (see https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted)
87+
# - +:results_as_hash+: boolean (default false), return rows as hashes instead of arrays
88+
# - +:type_translation+: boolean (default false), enable type translation
89+
# - +:default_transaction_mode+: one of +:deferred+ (default), +:immediate+, or +:exclusive+. If a mode is not specified in a call to #transaction, this will be the default transaction mode.
7690
#
77-
# By default, the new database will return result rows as arrays
78-
# (#results_as_hash) and has type translation disabled (#type_translation=).
79-
8091
def initialize file, options = {}, zvfs = nil
8192
mode = Constants::Open::READWRITE | Constants::Open::CREATE
8293

@@ -119,6 +130,7 @@ def initialize file, options = {}, zvfs = nil
119130
@type_translation = options[:type_translation]
120131
@type_translator = make_type_translator @type_translation
121132
@readonly = mode & Constants::Open::READONLY != 0
133+
@default_transaction_mode = options[:default_transaction_mode] || :deferred
122134

123135
if block_given?
124136
begin
@@ -622,8 +634,10 @@ def finalize
622634
# by SQLite, so attempting to nest a transaction will result in a runtime
623635
# exception.
624636
#
625-
# The +mode+ parameter may be either <tt>:deferred</tt> (the default),
637+
# The +mode+ parameter may be either <tt>:deferred</tt>,
626638
# <tt>:immediate</tt>, or <tt>:exclusive</tt>.
639+
# If `nil` is specified, the default transaction mode, which was
640+
# passed to #initialize, is used.
627641
#
628642
# If a block is given, the database instance is yielded to it, and the
629643
# transaction is committed when the block terminates. If the block
@@ -634,7 +648,8 @@ def finalize
634648
# If a block is not given, it is the caller's responsibility to end the
635649
# transaction explicitly, either by calling #commit, or by calling
636650
# #rollback.
637-
def transaction( mode = :deferred )
651+
def transaction( mode = nil )
652+
mode = @default_transaction_mode if mode.nil?
638653
execute "begin #{mode.to_s} transaction"
639654

640655
if block_given?

test/test_database.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,5 +624,45 @@ def test_raw_float_infinity
624624
db.execute("insert into foo values (?)", Float::INFINITY)
625625
assert_equal Float::INFINITY, db.execute("select avg(temperature) from foo").first.first
626626
end
627+
628+
def test_default_transaction_mode
629+
tf = Tempfile.new 'database_default_transaction_mode'
630+
SQLite3::Database.new(tf.path) do |db|
631+
db.execute("create table foo (score int)")
632+
db.execute("insert into foo values (?)", 1)
633+
end
634+
635+
test_cases = [
636+
{mode: nil, read: true, write: true},
637+
{mode: :deferred, read: true, write: true},
638+
{mode: :immediate, read: true, write: false},
639+
{mode: :exclusive, read: false, write: false},
640+
]
641+
642+
test_cases.each do |item|
643+
db = SQLite3::Database.new tf.path, default_transaction_mode: item[:mode]
644+
db2 = SQLite3::Database.new tf.path
645+
db.transaction do
646+
sql_for_read_test = "select * from foo"
647+
if item[:read]
648+
assert_nothing_raised{ db2.execute(sql_for_read_test) }
649+
else
650+
assert_raises(SQLite3::BusyException){ db2.execute(sql_for_read_test) }
651+
end
652+
653+
sql_for_write_test = "insert into foo values (2)"
654+
if item[:write]
655+
assert_nothing_raised{ db2.execute(sql_for_write_test) }
656+
else
657+
assert_raises(SQLite3::BusyException){ db2.execute(sql_for_write_test) }
658+
end
659+
end
660+
ensure
661+
db.close if db && !db.closed?
662+
db2.close if db2 && !db2.closed?
663+
end
664+
ensure
665+
tf.unlink if tf
666+
end
627667
end
628668
end

0 commit comments

Comments
 (0)