Transactions

* dsl, state checks
* define transaction sql commands in connection
This commit is contained in:
Brian J. Cardiff 2016-11-15 23:46:11 -03:00
parent 1049d95562
commit 751f8b26ac
9 changed files with 293 additions and 1 deletions

View file

@ -143,7 +143,10 @@ require "./db/session_methods"
require "./db/disposable"
require "./db/driver"
require "./db/statement"
require "./db/begin_transaction"
require "./db/connection"
require "./db/transaction"
require "./db/statement"
require "./db/pool_statement"
require "./db/database"
require "./db/pool_prepared_statement"

View file

@ -0,0 +1,17 @@
module DB
module BeginTransaction
abstract def begin_transaction : Transaction
def transaction
tx = begin_transaction
begin
yield tx
rescue e
tx.rollback
raise e unless e.is_a?(DB::Rollback)
else
tx.commit unless tx.closed?
end
end
end
end

View file

@ -21,10 +21,12 @@ module DB
abstract class Connection
include Disposable
include SessionMethods(Connection, Statement)
include BeginTransaction
# :nodoc:
getter database
@statements_cache = StringKeyCache(Statement).new
@transaction = false
getter? prepared_statements : Bool
def initialize(@database : Database)
@ -42,10 +44,45 @@ module DB
# :nodoc:
abstract def build_unprepared_statement(query) : Statement
def begin_transaction
raise DB::Error.new("There is an existing transaction in this connection") if @transaction
@transaction = true
create_transaction
end
protected def create_transaction : Transaction
TopLevelTransaction.new(self)
end
protected def do_close
@statements_cache.each_value &.close
@statements_cache.clear
@database.pool.delete self
end
# :nodoc:
def release_from_statement
@database.return_to_pool(self) unless @transaction
end
# :nodoc:
def release_from_transaction
@transaction = false
end
# :nodoc:
def perform_begin_transaction
self.unprepared.exec "BEGIN"
end
# :nodoc:
def perform_commit_transaction
self.unprepared.exec "COMMIT"
end
# :nodoc:
def perform_rollback_transaction
self.unprepared.exec "ROLLBACK"
end
end
end

View file

@ -105,6 +105,14 @@ module DB
end
end
def transaction
using_connection do |cnn|
cnn.transaction do |tx|
yield tx
end
end
end
# :nodoc:
def retry
@pool.retry do

View file

@ -17,4 +17,7 @@ module DB
def initialize(@connection)
end
end
class Rollback < Exception
end
end

View file

@ -59,7 +59,7 @@ module DB
end
def release_connection
@connection.database.return_to_pool(@connection)
@connection.release_from_statement
end
# See `QueryMethods#exec`

43
src/db/transaction.cr Normal file
View file

@ -0,0 +1,43 @@
module DB
abstract class Transaction
include Disposable
abstract def connection : Connection
def commit
close!
end
def rollback
close!
end
private def close!
raise DB::Error.new("Transaction already closed") if closed?
close
end
end
class TopLevelTransaction < Transaction
# :nodoc:
getter connection
def initialize(@connection : Connection)
@connection.perform_begin_transaction
end
def commit
@connection.perform_commit_transaction
close!
end
def rollback
@connection.perform_rollback_transaction
close!
end
protected def do_close
connection.release_from_transaction
end
end
end