Added nested transaction support.
Added test cases showing transaction support scenarios.
This commit is contained in:
@@ -131,6 +131,142 @@ feature -- Transactions
|
|||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature --
|
||||||
|
|
||||||
|
is_connected_to_storage: BOOLEAN
|
||||||
|
-- Is connected to the database
|
||||||
|
do
|
||||||
|
Result := db_control.is_connected
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Transaction Status
|
||||||
|
|
||||||
|
in_transaction_session: BOOLEAN
|
||||||
|
-- Is session started?
|
||||||
|
|
||||||
|
transaction_session_depth: INTEGER
|
||||||
|
-- Depth in the transaction session
|
||||||
|
|
||||||
|
feature -- Transaction Operation
|
||||||
|
|
||||||
|
begin_transaction2
|
||||||
|
-- Start session
|
||||||
|
-- if already started, increase the `transaction_session_depth'
|
||||||
|
require
|
||||||
|
in_transaction_session implies transaction_session_depth > 0
|
||||||
|
not in_transaction_session implies transaction_session_depth = 0
|
||||||
|
local
|
||||||
|
l_session_control: like db_control
|
||||||
|
l_retried: INTEGER
|
||||||
|
do
|
||||||
|
if l_retried = 0 then
|
||||||
|
if not in_transaction_session then
|
||||||
|
database_error_handler.reset
|
||||||
|
|
||||||
|
check transaction_session_depth = 0 end
|
||||||
|
|
||||||
|
debug ("database_session")
|
||||||
|
print ("..Start session%N")
|
||||||
|
end
|
||||||
|
connect -- connect the DB
|
||||||
|
if is_connected_to_storage then
|
||||||
|
in_transaction_session := True
|
||||||
|
db_control.begin -- start transaction
|
||||||
|
else
|
||||||
|
l_session_control := db_control
|
||||||
|
if not l_session_control.is_ok then
|
||||||
|
database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code)
|
||||||
|
else
|
||||||
|
database_error_handler.add_database_error ("Session_not_started_error_message", 0)
|
||||||
|
end
|
||||||
|
l_session_control.reset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
transaction_session_depth := transaction_session_depth + 1
|
||||||
|
else
|
||||||
|
if l_retried = 1 then
|
||||||
|
transaction_session_depth := transaction_session_depth + 1
|
||||||
|
if attached (create {EXCEPTION_MANAGER}).last_exception as e then
|
||||||
|
if attached {ASSERTION_VIOLATION} e then
|
||||||
|
--| Ignore for now with MYSQL ...
|
||||||
|
else
|
||||||
|
exception_as_error (e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
in_transaction_session := False
|
||||||
|
db_control.reset
|
||||||
|
else
|
||||||
|
in_transaction_session := False
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
transaction_session_depth = (old transaction_session_depth) + 1
|
||||||
|
rescue
|
||||||
|
l_retried := l_retried + 1
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
commit2
|
||||||
|
-- End session
|
||||||
|
local
|
||||||
|
l_retried: BOOLEAN
|
||||||
|
do
|
||||||
|
if not l_retried then
|
||||||
|
transaction_session_depth := transaction_session_depth - 1
|
||||||
|
if transaction_session_depth = 0 then
|
||||||
|
debug ("database_session")
|
||||||
|
print ("..End session%N")
|
||||||
|
end
|
||||||
|
if is_connected_to_storage then
|
||||||
|
if not database_error_handler.has_error then
|
||||||
|
db_control.commit -- Commit transaction
|
||||||
|
else
|
||||||
|
db_control.rollback -- Rollback transaction
|
||||||
|
end
|
||||||
|
end
|
||||||
|
in_transaction_session := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
|
||||||
|
in_transaction_session := False
|
||||||
|
transaction_session_depth := transaction_session_depth - 1
|
||||||
|
db_control.reset
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
l_retried := True
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rollback2
|
||||||
|
-- End session
|
||||||
|
local
|
||||||
|
l_retried: BOOLEAN
|
||||||
|
do
|
||||||
|
if not l_retried then
|
||||||
|
transaction_session_depth := transaction_session_depth - 1
|
||||||
|
if transaction_session_depth = 0 then
|
||||||
|
debug ("database_session")
|
||||||
|
print ("..End session%N")
|
||||||
|
end
|
||||||
|
if is_connected_to_storage then
|
||||||
|
db_control.rollback -- Rollback transaction
|
||||||
|
end
|
||||||
|
in_transaction_session := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
|
||||||
|
in_transaction_session := False
|
||||||
|
transaction_session_depth := transaction_session_depth - 1
|
||||||
|
db_control.reset
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
l_retried := True
|
||||||
|
retry
|
||||||
|
end
|
||||||
feature -- Change Element
|
feature -- Change Element
|
||||||
|
|
||||||
not_keep_connection
|
not_keep_connection
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
class
|
||||||
|
TRANSACTION_TEST_SET
|
||||||
|
|
||||||
|
inherit
|
||||||
|
|
||||||
|
EQA_TEST_SET
|
||||||
|
redefine
|
||||||
|
on_prepare,
|
||||||
|
on_clean
|
||||||
|
select
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
ABSTRACT_DB_TEST
|
||||||
|
rename
|
||||||
|
default_create as default_db_test
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature {NONE} -- Events
|
||||||
|
|
||||||
|
on_prepare
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
(create {CLEAN_DB}).clean_db(connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_clean
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Test routines
|
||||||
|
|
||||||
|
test_user_rollback
|
||||||
|
note
|
||||||
|
testing: "execution/isolated"
|
||||||
|
do
|
||||||
|
connection.begin_transaction
|
||||||
|
user_provider.new_user ("test", "test","test@admin.com")
|
||||||
|
assert ("Has user:", user_provider.has_user)
|
||||||
|
connection.rollback
|
||||||
|
assert ("Not has user:", not user_provider.has_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test_user_node_rollback
|
||||||
|
note
|
||||||
|
testing: "execution/isolated"
|
||||||
|
do
|
||||||
|
connection.begin_transaction
|
||||||
|
user_provider.new_user ("test", "test","test@admin.com")
|
||||||
|
assert ("Has user:", user_provider.has_user)
|
||||||
|
node_provider.new_node (default_node)
|
||||||
|
node_provider.add_author (1, 1)
|
||||||
|
assert ("Has one node:", node_provider.count = 1)
|
||||||
|
connection.rollback
|
||||||
|
assert ("Not has user:", not user_provider.has_user)
|
||||||
|
assert ("Not has nodes:", node_provider.count = 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test_user_node_nested_transaction_with_rollback_not_supported
|
||||||
|
note
|
||||||
|
testing: "execution/isolated"
|
||||||
|
do
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test", "test","test@admin.com")
|
||||||
|
assert ("Has user:", user_provider.has_user)
|
||||||
|
node_provider.new_node (default_node)
|
||||||
|
assert ("Has one node:", node_provider.count = 1)
|
||||||
|
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test1", "test1","test1@admin.com")
|
||||||
|
assert ("Has user: test1", attached user_provider.user_by_name ("test1"))
|
||||||
|
connection.rollback2
|
||||||
|
|
||||||
|
connection.commit2
|
||||||
|
assert ("Has user test:", attached user_provider.user_by_name ("test"))
|
||||||
|
assert ("Has nodes:", node_provider.count = 1)
|
||||||
|
assert ("Not has user: test1", user_provider.user_by_name ("test1") = Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
test_user_node_nested_transaction_with_commit
|
||||||
|
note
|
||||||
|
testing: "execution/isolated"
|
||||||
|
do
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test", "test","test@admin.com")
|
||||||
|
assert ("Has user:", user_provider.has_user)
|
||||||
|
node_provider.new_node (default_node)
|
||||||
|
assert ("Has one node:", node_provider.count = 1)
|
||||||
|
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test1", "test1","test1@admin.com")
|
||||||
|
assert ("Has user: test1", attached user_provider.user_by_name ("test1"))
|
||||||
|
connection.commit2
|
||||||
|
|
||||||
|
connection.commit2
|
||||||
|
assert ("Has user test:", attached user_provider.user_by_name ("test"))
|
||||||
|
assert ("Has user test1:", attached user_provider.user_by_name ("test1"))
|
||||||
|
assert ("Has nodes:", node_provider.count = 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
test_user_node_nested_transaction_with_rollback
|
||||||
|
note
|
||||||
|
testing: "execution/isolated"
|
||||||
|
do
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test", "test","test@admin.com")
|
||||||
|
assert ("Has user:", user_provider.has_user)
|
||||||
|
node_provider.new_node (default_node)
|
||||||
|
assert ("Has one node:", node_provider.count = 1)
|
||||||
|
|
||||||
|
connection.begin_transaction2
|
||||||
|
user_provider.new_user ("test1", "test1","test1@admin.com")
|
||||||
|
assert ("Has user: test1", attached user_provider.user_by_name ("test1"))
|
||||||
|
connection.commit2
|
||||||
|
|
||||||
|
connection.rollback2
|
||||||
|
assert ("Not Has user test:", user_provider.user_by_name ("test") = void)
|
||||||
|
assert ("Not Has user test1:", user_provider.user_by_name ("test1") = void)
|
||||||
|
assert ("Has 0 nodes:", node_provider.count = 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
node_provider: NODE_DATA_PROVIDER
|
||||||
|
-- node provider.
|
||||||
|
once
|
||||||
|
create Result.make (connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
user_provider: USER_DATA_PROVIDER
|
||||||
|
-- user provider.
|
||||||
|
once
|
||||||
|
create Result.make (connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation Fixture Factories
|
||||||
|
|
||||||
|
default_node: CMS_NODE
|
||||||
|
do
|
||||||
|
Result := custom_node ("Default content", "default summary", "Default")
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE
|
||||||
|
do
|
||||||
|
create Result.make (a_content, a_summary, a_title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user