From 109b6b15da72fdc3d4e61de25b64d104edfb35cb Mon Sep 17 00:00:00 2001 From: Masamitsu MURASE Date: Fri, 24 Nov 2023 15:09:46 +0900 Subject: [PATCH 1/3] Support default transaction mode. * Add "default_transaction_mode" to "options" parameter of SQLite3::Database.new. It is used as transaction mode when SQLite3::Database#transaction is called without "mode" argument. --- lib/sqlite3/database.rb | 8 ++++++-- test/test_database.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/sqlite3/database.rb b/lib/sqlite3/database.rb index c50ca8cf..cd03966c 100644 --- a/lib/sqlite3/database.rb +++ b/lib/sqlite3/database.rb @@ -119,6 +119,7 @@ def initialize file, options = {}, zvfs = nil @type_translation = options[:type_translation] @type_translator = make_type_translator @type_translation @readonly = mode & Constants::Open::READONLY != 0 + @default_transaction_mode = options[:default_transaction_mode] || :deferred if block_given? begin @@ -622,8 +623,10 @@ def finalize # by SQLite, so attempting to nest a transaction will result in a runtime # exception. # - # The +mode+ parameter may be either :deferred (the default), + # The +mode+ parameter may be either :deferred, # :immediate, or :exclusive. + # If `nil` is specified, the default transaction mode, which was + # passed to #initialize, is used. # # If a block is given, the database instance is yielded to it, and the # transaction is committed when the block terminates. If the block @@ -634,7 +637,8 @@ def finalize # If a block is not given, it is the caller's responsibility to end the # transaction explicitly, either by calling #commit, or by calling # #rollback. - def transaction( mode = :deferred ) + def transaction( mode = nil ) + mode = @default_transaction_mode if mode.nil? execute "begin #{mode.to_s} transaction" if block_given? diff --git a/test/test_database.rb b/test/test_database.rb index 81b10716..c6581b8c 100644 --- a/test/test_database.rb +++ b/test/test_database.rb @@ -624,5 +624,45 @@ def test_raw_float_infinity db.execute("insert into foo values (?)", Float::INFINITY) assert_equal Float::INFINITY, db.execute("select avg(temperature) from foo").first.first end + + def test_default_transaction_mode + tf = Tempfile.new 'database_default_transaction_mode' + SQLite3::Database.new(tf.path) do |db| + db.execute("create table foo (score int)") + db.execute("insert into foo values (?)", 1) + end + + test_cases = [ + {mode: nil, read: true, write: true}, + {mode: :deferred, read: true, write: true}, + {mode: :immediate, read: true, write: false}, + {mode: :exclusive, read: false, write: false}, + ] + + test_cases.each do |item| + db = SQLite3::Database.new tf.path, default_transaction_mode: item[:mode] + db2 = SQLite3::Database.new tf.path + db.transaction do + sql_for_read_test = "select * from foo" + if item[:read] + assert_nothing_raised{ db2.execute(sql_for_read_test) } + else + assert_raises(SQLite3::BusyException){ db2.execute(sql_for_read_test) } + end + + sql_for_write_test = "insert into foo values (2)" + if item[:write] + assert_nothing_raised{ db2.execute(sql_for_write_test) } + else + assert_raises(SQLite3::BusyException){ db2.execute(sql_for_write_test) } + end + end + ensure + db.close if db && !db.closed? + db2.close if db2 && !db2.closed? + end + ensure + tf.unlink if tf + end end end From f8ebfca3eefeb629d1a42d7c347313553415e6da Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 26 Nov 2023 12:53:05 -0500 Subject: [PATCH 2/3] doc: document Database.new options which includes the new :default_transaction_mode --- lib/sqlite3/database.rb | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/sqlite3/database.rb b/lib/sqlite3/database.rb index cd03966c..5d004a23 100644 --- a/lib/sqlite3/database.rb +++ b/lib/sqlite3/database.rb @@ -71,12 +71,23 @@ def quote( string ) # call-seq: SQLite3::Database.new(file, options = {}) # - # Create a new Database object that opens the given file. If utf16 - # is +true+, the filename is interpreted as a UTF-16 encoded string. + # Create a new Database object that opens the given file. + # + # Supported permissions +options+: + # - the default mode is READWRITE | CREATE + # - +:readonly+: boolean (default false), true to set the mode to +READONLY+ + # - +:readwrite+: boolean (default false), true to set the mode to +READWRITE+ + # - +:flags+: set the mode to a combination of SQLite3::Constants::Open flags. + # + # Supported encoding +options+: + # - +:utf16+: boolean (default false), is the filename's encoding UTF-16 (only needed if the filename encoding is not UTF_16LE or BE) + # + # Other supported +options+: + # - +: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) + # - +:results_as_hash+: boolean (default false), return rows as hashes instead of arrays + # - +:type_translation+: boolean (default false), enable type translation + # - +: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. # - # By default, the new database will return result rows as arrays - # (#results_as_hash) and has type translation disabled (#type_translation=). - def initialize file, options = {}, zvfs = nil mode = Constants::Open::READWRITE | Constants::Open::CREATE From f140292a7cb88539e862336f80b65088bf5fcbba Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 26 Nov 2023 12:59:31 -0500 Subject: [PATCH 3/3] doc: update CHANGELOG for default_transaction_mode --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f15d5e..56b12276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Vendored sqlite is update to [v3.44.2](https://sqlite.org/releaselog/3_44_2.html). @flavorjones +### Added + +- `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 + ## 1.6.8 / 2023-11-01