Skip to content

Commit

Permalink
Support transactions on MSSQL
Browse files Browse the repository at this point in the history
This commit supports transactions on MSSQL, which requires an
explicit TRANSACTION keyword to work.  It does this by adding private
Database methods (begin|rollback|commit)_transaction_sql, and
overridding them in the MSSQL shared adapter.  The default
transaction method uses these methods, and most of the other adapters
that override the transaction method also use these methods (the
PostgreSQL adapter being an exception).
  • Loading branch information
jeremyevans committed Sep 15, 2008
1 parent 0eb2fd7 commit b9eeb3b
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
@@ -1,5 +1,7 @@
=== HEAD === HEAD


* Support transactions on MSSQL (jeremyevans)

* Use string literals in AS clauses on SQLite (jeremyevans) (#241) * Use string literals in AS clauses on SQLite (jeremyevans) (#241)


* AlterTableGenerator#set_column_allow_null was added to SET/DROP NOT NULL for columns (divoxx) * AlterTableGenerator#set_column_allow_null was added to SET/DROP NOT NULL for columns (divoxx)
Expand Down
14 changes: 7 additions & 7 deletions lib/sequel_core/adapters/jdbc.rb
Expand Up @@ -154,18 +154,18 @@ def transaction(server=nil)
return yield(conn) if @transactions.include?(Thread.current) return yield(conn) if @transactions.include?(Thread.current)
stmt = conn.createStatement stmt = conn.createStatement
begin begin
log_info(Sequel::Database::SQL_BEGIN) log_info(begin_transaction_sql)
stmt.execute(Sequel::Database::SQL_BEGIN) stmt.execute(begin_transaction_sql)
@transactions << Thread.current @transactions << Thread.current
yield(conn) yield(conn)
rescue Exception => e rescue Exception => e
log_info(Sequel::Database::SQL_ROLLBACK) log_info(rollback_transaction_sql)
stmt.execute(Sequel::Database::SQL_ROLLBACK) stmt.execute(rollback_transaction_sql)
raise e unless Error::Rollback === e transaction_error(e)
ensure ensure
unless e unless e
log_info(Sequel::Database::SQL_COMMIT) log_info(commit_transaction_sql)
stmt.execute(Sequel::Database::SQL_COMMIT) stmt.execute(commit_transaction_sql)
end end
stmt.close stmt.close
@transactions.delete(Thread.current) @transactions.delete(Thread.current)
Expand Down
12 changes: 6 additions & 6 deletions lib/sequel_core/adapters/mysql.rb
Expand Up @@ -150,19 +150,19 @@ def server_version(server=nil)
def transaction(server=nil) def transaction(server=nil)
synchronize(server) do |conn| synchronize(server) do |conn|
return yield(conn) if @transactions.include?(Thread.current) return yield(conn) if @transactions.include?(Thread.current)
log_info(SQL_BEGIN) log_info(begin_transaction_sql)
conn.query(SQL_BEGIN) conn.query(begin_transaction_sql)
begin begin
@transactions << Thread.current @transactions << Thread.current
yield(conn) yield(conn)
rescue ::Exception => e rescue ::Exception => e
log_info(SQL_ROLLBACK) log_info(rollback_transaction_sql)
conn.query(SQL_ROLLBACK) conn.query(rollback_transaction_sql)
transaction_error(e, Mysql::Error) transaction_error(e, Mysql::Error)
ensure ensure
unless e unless e
log_info(SQL_COMMIT) log_info(commit_transaction_sql)
conn.query(SQL_COMMIT) conn.query(commit_transaction_sql)
end end
@transactions.delete(Thread.current) @transactions.delete(Thread.current)
end end
Expand Down
22 changes: 21 additions & 1 deletion lib/sequel_core/adapters/shared/mssql.rb
Expand Up @@ -2,16 +2,36 @@ module Sequel
module MSSQL module MSSQL
module DatabaseMethods module DatabaseMethods
AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
SQL_BEGIN = "BEGIN TRANSACTION".freeze
SQL_COMMIT = "COMMIT TRANSACTION".freeze
SQL_ROLLBACK = "ROLLBACK TRANSACTION".freeze


def auto_increment_sql def auto_increment_sql
AUTO_INCREMENT AUTO_INCREMENT
end end

def dataset(opts = nil) def dataset(opts = nil)
ds = super ds = super
ds.extend(DatasetMethods) ds.extend(DatasetMethods)
ds ds
end end

private

# SQL to BEGIN a transaction.
def begin_transaction_sql
SQL_BEGIN
end

# SQL to COMMIT a transaction.
def commit_transaction_sql
SQL_COMMIT
end

# SQL to ROLLBACK a transaction.
def rollback_transaction_sql
SQL_ROLLBACK
end
end end


module DatasetMethods module DatasetMethods
Expand Down
3 changes: 0 additions & 3 deletions lib/sequel_core/adapters/shared/mysql.rb
Expand Up @@ -7,9 +7,6 @@ module DatabaseMethods
NOT_NULL = Sequel::Schema::SQL::NOT_NULL NOT_NULL = Sequel::Schema::SQL::NOT_NULL
NULL = Sequel::Schema::SQL::NULL NULL = Sequel::Schema::SQL::NULL
PRIMARY_KEY = Sequel::Schema::SQL::PRIMARY_KEY PRIMARY_KEY = Sequel::Schema::SQL::PRIMARY_KEY
SQL_BEGIN = Sequel::Database::SQL_BEGIN
SQL_COMMIT = Sequel::Database::SQL_COMMIT
SQL_ROLLBACK = Sequel::Database::SQL_ROLLBACK
TYPES = Sequel::Schema::SQL::TYPES TYPES = Sequel::Schema::SQL::TYPES
UNIQUE = Sequel::Schema::SQL::UNIQUE UNIQUE = Sequel::Schema::SQL::UNIQUE
UNSIGNED = Sequel::Schema::SQL::UNSIGNED UNSIGNED = Sequel::Schema::SQL::UNSIGNED
Expand Down
63 changes: 39 additions & 24 deletions lib/sequel_core/database.rb
Expand Up @@ -355,27 +355,27 @@ def test_connection(server=nil)
synchronize(server){|conn|} synchronize(server){|conn|}
true true
end end

# A simple implementation of SQL transactions. Nested transactions are not # A simple implementation of SQL transactions. Nested transactions are not
# supported - calling #transaction within a transaction will reuse the # supported - calling #transaction within a transaction will reuse the
# current transaction. Should be overridden for databases that support nested # current transaction. Should be overridden for databases that support nested
# transactions. # transactions.
def transaction(server=nil) def transaction(server=nil)
synchronize(server) do |conn| synchronize(server) do |conn|
return yield(conn) if @transactions.include?(Thread.current) return yield(conn) if @transactions.include?(Thread.current)
log_info(SQL_BEGIN) log_info(begin_transaction_sql)
conn.execute(SQL_BEGIN) conn.execute(begin_transaction_sql)
begin begin
@transactions << Thread.current @transactions << Thread.current
yield(conn) yield(conn)
rescue Exception => e rescue Exception => e
log_info(SQL_ROLLBACK) log_info(rollback_transaction_sql)
conn.execute(SQL_ROLLBACK) conn.execute(rollback_transaction_sql)
transaction_error(e) transaction_error(e)
ensure ensure
unless e unless e
log_info(SQL_COMMIT) log_info(commit_transaction_sql)
conn.execute(SQL_COMMIT) conn.execute(commit_transaction_sql)
end end
@transactions.delete(Thread.current) @transactions.delete(Thread.current)
end end
Expand Down Expand Up @@ -471,6 +471,38 @@ def uri


private private


# SQL to BEGIN a transaction.
def begin_transaction_sql
SQL_BEGIN
end

# SQL to COMMIT a transaction.
def commit_transaction_sql
SQL_COMMIT
end

# The default options for the connection pool.
def connection_pool_default_options
{}
end

# SQL to ROLLBACK a transaction.
def rollback_transaction_sql
SQL_ROLLBACK
end

# Convert the given exception to a DatabaseError, keeping message
# and traceback.
def raise_error(exception, opts={})
if !opts[:classes] || exception.is_one_of?(*opts[:classes])
e = DatabaseError.new("#{exception.class} #{exception.message}")
e.set_backtrace(exception.backtrace)
raise e
else
raise exception
end
end

# Return the options for the given server by merging the generic # Return the options for the given server by merging the generic
# options for all server with the specific options for the given # options for all server with the specific options for the given
# server specified in the :servers option. # server specified in the :servers option.
Expand All @@ -491,23 +523,6 @@ def server_opts(server)
opts opts
end end


# The default options for the connection pool.
def connection_pool_default_options
{}
end

# Convert the given exception to a DatabaseError, keeping message
# and traceback.
def raise_error(exception, opts={})
if !opts[:classes] || exception.is_one_of?(*opts[:classes])
e = DatabaseError.new("#{exception.class} #{exception.message}")
e.set_backtrace(exception.backtrace)
raise e
else
raise exception
end
end

# Raise a database error unless the exception is an Error::Rollback. # Raise a database error unless the exception is an Error::Rollback.
def transaction_error(e, *classes) def transaction_error(e, *classes)
raise_error(e, :classes=>classes) unless Error::Rollback === e raise_error(e, :classes=>classes) unless Error::Rollback === e
Expand Down

0 comments on commit b9eeb3b

Please sign in to comment.