Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support default transaction mode. #426

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
29 changes: 22 additions & 7 deletions lib/sqlite3/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 <tt>READWRITE | CREATE</tt>
# - +: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

Expand Down Expand Up @@ -119,6 +130,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
Expand Down Expand Up @@ -622,8 +634,10 @@ def finalize
# by SQLite, so attempting to nest a transaction will result in a runtime
# exception.
#
# The +mode+ parameter may be either <tt>:deferred</tt> (the default),
# The +mode+ parameter may be either <tt>:deferred</tt>,
# <tt>:immediate</tt>, or <tt>:exclusive</tt>.
# 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
Expand All @@ -634,7 +648,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?
Expand Down
40 changes: 40 additions & 0 deletions test/test_database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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