|
|
|
|
@@ -0,0 +1,371 @@
|
|
|
|
|
note
|
|
|
|
|
description: "[
|
|
|
|
|
Manager to initialize data api for database access,
|
|
|
|
|
create database connection and so on
|
|
|
|
|
]"
|
|
|
|
|
date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $"
|
|
|
|
|
revision: "$Revision: 195 $"
|
|
|
|
|
|
|
|
|
|
class
|
|
|
|
|
DATABASE_STORAGE_MANAGER
|
|
|
|
|
|
|
|
|
|
inherit
|
|
|
|
|
GLOBAL_SETTINGS
|
|
|
|
|
|
|
|
|
|
REFACTORING_HELPER
|
|
|
|
|
|
|
|
|
|
SHARED_ERROR_HANDLER
|
|
|
|
|
|
|
|
|
|
create
|
|
|
|
|
make
|
|
|
|
|
|
|
|
|
|
feature -- Initialization
|
|
|
|
|
|
|
|
|
|
make (a_data_app: like data_app; a_database_name: like database_name; a_name: like name; a_password: like password;
|
|
|
|
|
a_host_name: like host_name; a_role_id: like role_id; a_role_password: like role_password
|
|
|
|
|
a_data_source: like data_source; a_group: like group)
|
|
|
|
|
-- Initialize with login info.
|
|
|
|
|
--
|
|
|
|
|
-- `a_database_name' is used for MySQL
|
|
|
|
|
-- `a_name', the login user name
|
|
|
|
|
-- `a_password', the login user password, unencrypted.
|
|
|
|
|
local
|
|
|
|
|
l_storage: STRING_8
|
|
|
|
|
do
|
|
|
|
|
create l_storage.make (64)
|
|
|
|
|
storage_url := l_storage
|
|
|
|
|
l_storage.append (a_data_app.db_spec.database_handle_name.as_lower)
|
|
|
|
|
l_storage.append ("://")
|
|
|
|
|
|
|
|
|
|
data_app := a_data_app
|
|
|
|
|
database_name := a_database_name
|
|
|
|
|
name := a_name
|
|
|
|
|
password := a_password
|
|
|
|
|
host_name := a_host_name
|
|
|
|
|
role_id := a_role_id
|
|
|
|
|
role_password := a_role_password
|
|
|
|
|
data_source := a_data_source
|
|
|
|
|
group := a_group
|
|
|
|
|
|
|
|
|
|
set_use_extended_types (True)
|
|
|
|
|
set_map_zero_null_value (False)
|
|
|
|
|
|
|
|
|
|
l_storage.append (a_name.as_string_8)
|
|
|
|
|
l_storage.append (":********@")
|
|
|
|
|
if a_host_name /= Void then
|
|
|
|
|
a_data_app.set_hostname (a_host_name)
|
|
|
|
|
l_storage.append (a_host_name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if a_database_name /= Void then
|
|
|
|
|
a_data_app.set_application (a_database_name.as_string_8)
|
|
|
|
|
l_storage.append ("/" + a_database_name.as_string_8)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if a_data_source /= Void then
|
|
|
|
|
a_data_app.set_data_source (a_data_source)
|
|
|
|
|
end
|
|
|
|
|
if a_role_id /= Void and then a_role_password /= Void then
|
|
|
|
|
a_data_app.set_role (a_role_id, a_role_password)
|
|
|
|
|
end
|
|
|
|
|
if a_group /= Void then
|
|
|
|
|
a_data_app.set_group (a_group)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
a_data_app.login (a_name.as_string_8, a_password.as_string_8)
|
|
|
|
|
a_data_app.set_base
|
|
|
|
|
|
|
|
|
|
create session_control.make
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
report_database_schema_incompatibility (a_output: BOOLEAN)
|
|
|
|
|
-- Report the application code is not compatible with database schema version
|
|
|
|
|
-- if `a_output' is True, write error in io.error as well
|
|
|
|
|
require
|
|
|
|
|
-- incompatible_database_schema_version: not is_database_schema_version_compatible
|
|
|
|
|
local
|
|
|
|
|
db_v: READABLE_STRING_8
|
|
|
|
|
do
|
|
|
|
|
-- if attached database_schema_version as v then
|
|
|
|
|
-- db_v := v.version
|
|
|
|
|
-- else
|
|
|
|
|
-- db_v := "?.?.?.?"
|
|
|
|
|
-- end
|
|
|
|
|
-- database_error_handler.add_error_details (0, "MISC Error", "Schema version incompatible (application="
|
|
|
|
|
-- + database_storage_version.version + " database=" + db_v + ")."
|
|
|
|
|
-- )
|
|
|
|
|
if a_output then
|
|
|
|
|
io.error.put_string (database_error_handler.as_string_representation)
|
|
|
|
|
io.error.put_new_line
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
feature -- System Update
|
|
|
|
|
|
|
|
|
|
update_system
|
|
|
|
|
do
|
|
|
|
|
-- if is_database_schema_version_compatible then
|
|
|
|
|
-- misc_manager.initialize_reference_types
|
|
|
|
|
-- history_manager.initialize_data
|
|
|
|
|
-- user_role_permission_manager.initialize_built_in_user_role_permission
|
|
|
|
|
-- user_role_permission_manager.initialize_extra_user_role_permission
|
|
|
|
|
-- task_manager.initialize_data
|
|
|
|
|
-- else
|
|
|
|
|
-- -- If schema incompatible, report it and exit
|
|
|
|
|
-- report_database_schema_incompatibility (True)
|
|
|
|
|
-- (create {EXCEPTIONS}).die (-1)
|
|
|
|
|
-- end
|
|
|
|
|
|
|
|
|
|
-- [2012-Mars-21] Idea about update system implementation
|
|
|
|
|
-- if update_version < 01.00.0012 then
|
|
|
|
|
-- if update_version < 01.00.0005 then
|
|
|
|
|
-- update_version_01_00_0005
|
|
|
|
|
-- end
|
|
|
|
|
-- update_version_01_00_0012
|
|
|
|
|
-- end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
reset_storage_manager
|
|
|
|
|
do
|
|
|
|
|
-- initialize_managers (Current)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
feature -- Storage
|
|
|
|
|
|
|
|
|
|
storage_url: READABLE_STRING_8
|
|
|
|
|
-- Associated storage URL
|
|
|
|
|
|
|
|
|
|
storage_connection_kept_alive: BOOLEAN
|
|
|
|
|
-- Keep storage connection alive?
|
|
|
|
|
-- i.e: never disconnect between 2 transactions.
|
|
|
|
|
|
|
|
|
|
keep_storage_connection_alive (b: BOOLEAN)
|
|
|
|
|
do
|
|
|
|
|
storage_connection_kept_alive := b
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
connect_storage
|
|
|
|
|
-- Connect the database
|
|
|
|
|
do
|
|
|
|
|
if not session_control.is_connected then
|
|
|
|
|
session_control.connect
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
disconnect_from_storage
|
|
|
|
|
-- Disconnect from the storage
|
|
|
|
|
require
|
|
|
|
|
is_connected_to_storage: is_connected_to_storage
|
|
|
|
|
do
|
|
|
|
|
if not storage_connection_kept_alive then
|
|
|
|
|
session_control.disconnect
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
force_disconnect_from_storage
|
|
|
|
|
-- Force disconnection from the storage
|
|
|
|
|
-- i.e ignore any `storage_connection_kept_alive'
|
|
|
|
|
do
|
|
|
|
|
if session_control.is_connected then
|
|
|
|
|
session_control.disconnect
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
is_connected_to_storage: BOOLEAN
|
|
|
|
|
-- Is connected to the database
|
|
|
|
|
do
|
|
|
|
|
Result := session_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
|
|
|
|
|
|
|
|
|
|
start_transaction_session
|
|
|
|
|
-- 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 session_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_storage -- connect the DB
|
|
|
|
|
if is_connected_to_storage then
|
|
|
|
|
in_transaction_session := True
|
|
|
|
|
session_control.begin -- start transaction
|
|
|
|
|
else
|
|
|
|
|
l_session_control := session_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
|
|
|
|
|
session_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
|
|
|
|
|
|
|
|
|
|
end_transaction_session
|
|
|
|
|
-- 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
|
|
|
|
|
session_control.commit -- Commit transaction
|
|
|
|
|
else
|
|
|
|
|
session_control.rollback -- Rollback transaction
|
|
|
|
|
end
|
|
|
|
|
disconnect_from_storage
|
|
|
|
|
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
|
|
|
|
|
session_control.reset
|
|
|
|
|
end
|
|
|
|
|
rescue
|
|
|
|
|
l_retried := True
|
|
|
|
|
retry
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
execute_query (a_query: STRING_32)
|
|
|
|
|
-- Execute `a_q'
|
|
|
|
|
require
|
|
|
|
|
is_session_started: in_transaction_session
|
|
|
|
|
local
|
|
|
|
|
rescued: BOOLEAN
|
|
|
|
|
l_change: like new_database_change
|
|
|
|
|
do
|
|
|
|
|
if not rescued then
|
|
|
|
|
session_control.reset
|
|
|
|
|
l_change := new_database_change
|
|
|
|
|
l_change.set_query (a_query)
|
|
|
|
|
l_change.execute_query
|
|
|
|
|
else
|
|
|
|
|
database_error_handler.add_error_details (0, "Unexpected Error", "Unexpected Error when executing query")
|
|
|
|
|
end
|
|
|
|
|
rescue
|
|
|
|
|
rescued := True
|
|
|
|
|
retry
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
feature -- Element Change
|
|
|
|
|
|
|
|
|
|
set_last_inserted_id_function (a_f: like last_inserted_id_function)
|
|
|
|
|
-- Set `last_inserted_id_function' with `a_f'
|
|
|
|
|
do
|
|
|
|
|
last_inserted_id_function := a_f
|
|
|
|
|
ensure
|
|
|
|
|
last_inserted_id_function_set: last_inserted_id_function = a_f
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
feature -- Access
|
|
|
|
|
|
|
|
|
|
database_name: detachable READABLE_STRING_GENERAL
|
|
|
|
|
-- Database to access
|
|
|
|
|
|
|
|
|
|
name: READABLE_STRING_GENERAL
|
|
|
|
|
-- Login user name
|
|
|
|
|
|
|
|
|
|
password: READABLE_STRING_8
|
|
|
|
|
-- Password
|
|
|
|
|
|
|
|
|
|
host_name: detachable READABLE_STRING_8
|
|
|
|
|
-- Host name, and port if needed
|
|
|
|
|
|
|
|
|
|
role_id: detachable READABLE_STRING_8
|
|
|
|
|
-- Role id
|
|
|
|
|
|
|
|
|
|
role_password: detachable READABLE_STRING_8
|
|
|
|
|
-- Role password
|
|
|
|
|
|
|
|
|
|
data_source: detachable READABLE_STRING_8
|
|
|
|
|
-- Data source
|
|
|
|
|
|
|
|
|
|
group: detachable READABLE_STRING_8
|
|
|
|
|
-- Group
|
|
|
|
|
|
|
|
|
|
data_app: DATABASE_APPL [DATABASE]
|
|
|
|
|
-- Database application
|
|
|
|
|
|
|
|
|
|
last_inserted_id_function: detachable FUNCTION [ANY, TUPLE, NATURAL_64]
|
|
|
|
|
-- Function to get last inserted id.
|
|
|
|
|
|
|
|
|
|
feature -- Factory
|
|
|
|
|
|
|
|
|
|
new_database_change: DB_CHANGE
|
|
|
|
|
-- Database change
|
|
|
|
|
do
|
|
|
|
|
create Result.make
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
new_database_selection: DB_SELECTION
|
|
|
|
|
-- Database selection
|
|
|
|
|
do
|
|
|
|
|
create Result.make
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
new_procedure (a_name: like {DB_PROC}.name): DB_PROC
|
|
|
|
|
-- Database procedure
|
|
|
|
|
do
|
|
|
|
|
create Result.make (a_name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
feature {NONE} -- Implementation
|
|
|
|
|
|
|
|
|
|
session_not_started_error_message: STRING_32 = "Session could not be started for unknown reason"
|
|
|
|
|
|
|
|
|
|
session_control: DB_CONTROL
|
|
|
|
|
-- Session
|
|
|
|
|
|
|
|
|
|
end
|