Fixed persistency layer.

Now we have ODBC .. that accepts various connection string (including SQLite, MySQL,...)
  And EiffelStore+MySQL.
Updated sql scripts to work with MySQL, and SQLite.
Added a sql_statement (s: STRING): STRING that converts ROC sql statement to fit the underlying database engine.
 mostly to adapt incompatibilities such as AUTO_INCREMENT for MySQL and AUTOINCREMENT for SQLite
 by default SQL script should be written following MySQL SQL syntax.
Warning: to use ODBC persistence driver, it has to be installed on the target machine.
This commit is contained in:
2015-06-18 13:55:05 +02:00
parent e37dbb0a62
commit f619727997
45 changed files with 1235 additions and 662 deletions

View File

@@ -19,7 +19,8 @@
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="persistence_sqlite" location="..\..\library\persistence\sqlite\persistence_sqlite-safe.ecf" readonly="false"/>
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target>
@@ -38,6 +39,7 @@
<option debug="true">
<debug name="dbglog" enabled="true"/>
</option>
<variable name="httpd_ssl_disabled" value="true"/><!-- for now ... due to issue with libcurl+eiffelnet ssl -->
<setting name="concurrency" value="thread"/>
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>

View File

@@ -68,10 +68,10 @@ feature {CMS_API} -- Module management
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then
sql := "[
CREATE TABLE "blog_post_nodes"(
"nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0),
"revision" INTEGER,
"tags" VARCHAR(255)
CREATE TABLE blog_post_nodes(
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK("nid">=0),
`revision` INTEGER,
`tags` VARCHAR(255)
);
]"
l_sql_storage.sql_execute_script (sql)

View File

@@ -63,10 +63,10 @@ feature {CMS_API} -- Module management
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("tb_demo") then
sql := "[
CREATE TABLE "tb_demo"(
"demo_id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("demo_id">=0),
"name" VARCHAR(100) NOT NULL,
"value" TEXT
CREATE TABLE tb_demo(
`demo_id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK("demo_id">=0),
`name` VARCHAR(100) NOT NULL,
`value` TEXT
);
]"
l_sql_storage.sql_execute_script (sql)

View File

@@ -1,15 +1,18 @@
{
"database": {
"datasource": {
"driver": "sqlite",
"environment": "sqlite"
"driver": "odbc",
"environment": "odbc-sqlite"
},
"environments": {
"sqlite": {
"odbc-sqlite": {
"connection_string":"Driver=SQLite3 ODBC Driver;Database=./site/database.sqlite;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;"
},
"test": {
"connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;"
"odbc-mysql": {
"connection_string":"Driver=mysql ODBC Driver;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;"
},
"mysql": {
"connection_string":"Driver=mysql;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;"
},
"development": {
"connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;"

View File

@@ -1,28 +1,23 @@
BEGIN;
CREATE TABLE "logs"(
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0),
"category" VARCHAR(255) NOT NULL,
"level" INTEGER NOT NULL CHECK("level">=1),
"uid" INTEGER,
"message" TEXT NOT NULL,
"info" TEXT,
"link" TEXT,
"date" DATETIME NOT NULL
CREATE TABLE `logs`(
`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`category` VARCHAR(255) NOT NULL,
`level` INTEGER NOT NULL,
`uid` INTEGER,
`message` TEXT NOT NULL,
`info` TEXT,
`link` TEXT,
`date` DATETIME NOT NULL
);
CREATE TABLE "custom_values"(
"type" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"value" TEXT
CREATE TABLE `custom_values`(
`type` VARCHAR(255) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`value` TEXT
);
CREATE TABLE "path_aliases"(
"pid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("pid">=0),
"source" VARCHAR(255) NOT NULL,
"alias" VARCHAR(255) NOT NULL,
"lang" VARCHAR(12)
CREATE TABLE `path_aliases`(
`pid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`source` VARCHAR(255) NOT NULL,
`alias` VARCHAR(255) NOT NULL,
`lang` VARCHAR(12)
);
COMMIT;

View File

@@ -1,24 +1,22 @@
BEGIN;
CREATE TABLE "nodes" (
"nid" INTEGER NOT NULL CHECK("nid" > 0) PRIMARY KEY AUTOINCREMENT UNIQUE,
"revision" INTEGER,
"type" TEXT NOT NULL,
"title" VARCHAR(255) NOT NULL,
"summary" TEXT,
"content" TEXT,
"format" VARCHAR(128),
"author" INTEGER,
"publish" DATETIME,
"created" DATETIME NOT NULL,
"changed" DATETIME NOT NULL,
"status" INTEGER
CREATE TABLE nodes (
`nid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
`revision` INTEGER,
`type` TEXT NOT NULL,
`title` VARCHAR(255) NOT NULL,
`summary` TEXT,
`content` TEXT,
`format` VARCHAR(128),
`author` INTEGER,
`publish` DATETIME,
`created` DATETIME NOT NULL,
`changed` DATETIME NOT NULL,
`status` INTEGER
);
CREATE TABLE page_nodes(
"nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0),
"revision" INTEGER,
"parent" INTEGER
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`revision` INTEGER,
`parent` INTEGER
);
COMMIT;

View File

@@ -1,34 +1,32 @@
BEGIN;
CREATE TABLE "users"(
"uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0),
"name" VARCHAR(100) NOT NULL,
"password" VARCHAR(100) NOT NULL,
"salt" VARCHAR(100) NOT NULL,
"email" VARCHAR(250) NOT NULL,
"status" INTEGER,
"created" DATETIME NOT NULL,
"signed" DATETIME,
CONSTRAINT "name"
UNIQUE("name")
CREATE TABLE `users`(
`uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`status` INTEGER,
`created` DATETIME NOT NULL,
`signed` DATETIME,
CONSTRAINT `name`
UNIQUE(`name`)
);
CREATE TABLE "roles"(
"rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0),
"name" VARCHAR(100) NOT NULL,
CONSTRAINT "name"
UNIQUE("name")
CREATE TABLE `roles`(
`rid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
CONSTRAINT `name`
UNIQUE(`name`)
);
CREATE TABLE "users_roles"(
"uid" INTEGER NOT NULL CHECK("uid">=0),
"rid" INTEGER NOT NULL CHECK("rid">=0)
CREATE TABLE `users_roles`(
`uid` INTEGER NOT NULL CHECK(`uid`>=0),
`rid` INTEGER NOT NULL CHECK(`rid`>=0)
);
CREATE TABLE "role_permissions"(
"rid" INTEGER NOT NULL CHECK("rid">=0),
"permission" VARCHAR(255) NOT NULL,
"module" VARCHAR(255)
CREATE TABLE `role_permissions`(
`rid` INTEGER NOT NULL,
`permission` VARCHAR(255) NOT NULL,
`module` VARCHAR(255)
);
COMMIT;

View File

@@ -46,8 +46,8 @@ feature -- CMS setup
debug ("refactor_fixme")
to_implement ("To implement custom storage")
end
-- a_setup.storage_drivers.force (create {CMS_STORAGE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE_BUILDER}.make, "sqlite")
-- a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
end
setup_modules (a_setup: CMS_SETUP)

View File

@@ -8,16 +8,16 @@ deferred class
feature -- Database access
hostname: STRING = ""
default_hostname: STRING = ""
-- Database hostname.
username: STRING = ""
default_username: STRING = ""
-- Database username.
password: STRING = ""
default_password: STRING = ""
-- Database password.
database_name: STRING = "EiffelDB"
default_database_name: STRING = "EiffelDB"
-- Database name.
is_keep_connection: BOOLEAN

View File

@@ -12,27 +12,7 @@ inherit
feature {NONE} -- Initialization
make_common
-- Create a database handler with common settings.
deferred
ensure
db_application_not_void: db_application /= Void
db_control_not_void: db_control /= Void
end
make_basic ( a_database_name: STRING)
-- Create a database handler with common settings and
-- set database_name with `a_database_name'.
require
database_name_not_void: a_database_name /= Void
database_name_not_empty: not a_database_name.is_empty
deferred
ensure
db_application_not_void: db_application /= Void
db_control_not_void: db_control /= Void
end
make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN)
login (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN)
-- Create a database handler with user `a_username', password `a_password',
-- host `a_hostname', database_name `a_database_name', and keep_connection `connection'.
@@ -59,6 +39,26 @@ feature {NONE} -- Initialization
db_control_not_void: db_control /= Void
end
login_with_default
-- Create a database handler with common settings.
deferred
ensure
db_application_not_void: db_application /= Void
db_control_not_void: db_control /= Void
end
login_with_database_name ( a_database_name: STRING)
-- Create a database handler with common settings and
-- set database_name with `a_database_name'.
require
database_name_not_void: a_database_name /= Void
database_name_not_empty: not a_database_name.is_empty
deferred
ensure
db_application_not_void: db_application /= Void
db_control_not_void: db_control /= Void
end
feature -- Database Setup
db_application: DATABASE_APPL [DATABASE]
@@ -179,11 +179,11 @@ feature -- Status Report
feature -- Helper
exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception)
-- Record exception as an error.
exception_as_error (a_exception: like {EXCEPTION_MANAGER}.last_exception)
-- Record exception `a_exception' as an error.
do
if attached a_e as l_e and then attached l_e.trace as l_trace then
database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32)
if a_exception /= Void and then attached a_exception.trace as l_trace then
database_error_handler.add_error_details (a_exception.code, once "Exception", l_trace)
end
end

View File

@@ -7,118 +7,98 @@ class
DATABASE_CONNECTION_ODBC
inherit
DATABASE_CONNECTION
redefine
db_application
end
SHARED_LOGGER
create
make, make_common, make_basic, login_with_connection_string
login, login_with_default, login_with_database_name, login_with_connection_string
feature -- Initialization
make_common
-- Create a database handler for ODBC with common settings.
local
l_retried: BOOLEAN
do
create db_application.login (username, password)
create database_error_handler.make
if not l_retried then
db_application.set_hostname (hostname)
db_application.set_data_source (database_name)
db_application.set_base
create db_control.make
keep_connection := is_keep_connection
if keep_connection then
connect
end
else
create db_control.make
end
rescue
create db_control.make
-- set_last_error_from_exception ("Connection execution")
-- write_critical_log (generator + ".make_common:" + last_error_message)
if is_connected then
disconnect
end
l_retried := True
retry
end
make_basic (a_database_name: STRING)
-- Create a database handler and
-- set database_name to `a_database_name'.
local
l_retried: BOOLEAN
do
create db_application.login (username, password)
create database_error_handler.make
if not l_retried then
db_application.set_hostname (hostname)
db_application.set_data_source (a_database_name)
db_application.set_base
create db_control.make
keep_connection := is_keep_connection
if keep_connection then
connect
end
else
create db_control.make
end
rescue
create db_control.make
-- set_last_error_from_exception ("Connection execution")
-- write_critical_log (generator + ".make_common:" + last_error_message)
if is_connected then
disconnect
end
l_retried := True
retry
end
make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN)
login (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; a_keep_connection: BOOLEAN)
-- Create a database handler for ODBC and set `username' to `a_username',
-- `password' to `a_password'
-- `database_name' to `a_database_name'
-- `connection' to `a_connection'
-- `keep_connection' to `a_keep_connection'
local
retried: BOOLEAN
l_database_error_handler: detachable like database_error_handler
do
create database_error_handler.make
create l_database_error_handler.make
database_error_handler := l_database_error_handler
create db_application.login (a_username, a_password)
if not retried then
db_application.set_hostname (a_hostname)
db_application.set_data_source (a_database_name)
db_application.set_base
create db_control.make
keep_connection := connection
keep_connection := a_keep_connection
if keep_connection then
connect
end
else
create db_control.make
if is_connected then
disconnect
end
end
rescue
if l_database_error_handler = Void then
create l_database_error_handler.make
end
database_error_handler := l_database_error_handler
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
retried := True
retry
end
login_with_default
-- Create a database handler for ODBC with common settings.
do
login_with_database_name (default_database_name)
end
login_with_database_name (a_database_name: STRING)
-- Create a database handler and
-- set database_name to `a_database_name'.
do
login (default_username, default_password, default_hostname, default_database_name, is_keep_connection)
end
login_with_connection_string (a_string: STRING)
-- Login with `a_connection_string'and immediately connect to database.
local
retried: BOOLEAN
l_database_error_handler: detachable like database_error_handler
do
write_debug_log (generator +".login_with_connection_string")
create l_database_error_handler.make
database_error_handler := l_database_error_handler
create db_application.login_with_connection_string (a_string)
create database_error_handler.make
if not retried then
db_application.set_base
create db_control.make
write_debug_log (generator +".login_with_connection_string, is_keep_connection? "+ is_keep_connection.out )
keep_connection := is_keep_connection
if keep_connection then
connect
if not db_control.is_ok then
write_critical_log (generator +".login_with_connection_string:"+ db_control.error_code.out )
write_critical_log (generator +".login_with_connection_string:"+ db_control.error_message_32 )
end
write_debug_log (generator +".login_with_connection_string, After connect, is_connected? "+ is_connected.out)
else
create db_control.make
if is_connected then
disconnect
end
end
rescue
if l_database_error_handler = Void then
create l_database_error_handler.make
end
database_error_handler := l_database_error_handler
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
retried := True
retry
end
feature -- Databse Connection

View File

@@ -1,3 +0,0 @@
To build the MySQL schema use the schema.sql script.
The other script will be deleted soon.

View File

@@ -1 +0,0 @@
Added Stored Procedures

View File

@@ -1,163 +0,0 @@
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
-- -----------------------------------------------------
-- Schema cms_dev
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `cms_dev` DEFAULT CHARACTER SET latin1 ;
USE `cms_dev` ;
-- -----------------------------------------------------
-- Table `cms_dev`.`users`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`users` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`creation_date` DATETIME NULL DEFAULT NULL,
`last_login_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `username` (`username` ASC))
ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`nodes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`publication_date` DATE NOT NULL,
`creation_date` DATE NOT NULL,
`modification_date` DATE NOT NULL,
`title` VARCHAR(255) NOT NULL,
`summary` TEXT NOT NULL,
`content` MEDIUMTEXT NOT NULL,
`author_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`version` INT(10) UNSIGNED ZEROFILL NULL DEFAULT NULL,
`editor_id` INT(10) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `fk_nodes_users1_idx` (`author_id` ASC),
INDEX `fk_nodes_users2_idx` (`editor_id` ASC),
CONSTRAINT `fk_nodes_users1`
FOREIGN KEY (`author_id`)
REFERENCES `cms_dev`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_nodes_users2`
FOREIGN KEY (`editor_id`)
REFERENCES `cms_dev`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
AUTO_INCREMENT = 11
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`roles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`roles` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`role` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `role` (`role` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`permissions`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`permissions` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
`roles_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `name_UNIQUE` (`name` ASC),
INDEX `fk_permissions_roles1_idx` (`roles_id` ASC),
CONSTRAINT `fk_permissions_roles1`
FOREIGN KEY (`roles_id`)
REFERENCES `cms_dev`.`roles` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`profiles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`profiles` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`key` VARCHAR(45) NOT NULL,
`value` VARCHAR(100) NULL DEFAULT NULL,
`users_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `key_UNIQUE` (`key` ASC),
INDEX `fk_profiles_users1_idx` (`users_id` ASC),
CONSTRAINT `fk_profiles_users1`
FOREIGN KEY (`users_id`)
REFERENCES `cms_dev`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`users_nodes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`users_nodes` (
`users_id` INT(10) UNSIGNED NOT NULL,
`nodes_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`users_id`, `nodes_id`),
INDEX `fk_users_has_nodes_nodes1_idx` (`nodes_id` ASC),
INDEX `fk_users_has_nodes_users_idx` (`users_id` ASC),
CONSTRAINT `fk_users_has_nodes_nodes1`
FOREIGN KEY (`nodes_id`)
REFERENCES `cms_dev`.`nodes` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_users_has_nodes_users`
FOREIGN KEY (`users_id`)
REFERENCES `cms_dev`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `cms_dev`.`users_roles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cms_dev`.`users_roles` (
`users_id` INT(10) UNSIGNED NOT NULL,
`roles_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`users_id`, `roles_id`),
INDEX `fk_users_has_roles_roles1_idx` (`roles_id` ASC),
INDEX `fk_users_has_roles_users1_idx` (`users_id` ASC),
CONSTRAINT `fk_users_has_roles_roles1`
FOREIGN KEY (`roles_id`)
REFERENCES `cms_dev`.`roles` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_users_has_roles_users1`
FOREIGN KEY (`users_id`)
REFERENCES `cms_dev`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

View File

@@ -1,72 +0,0 @@
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
-- -----------------------------------------------------
-- Schema roc_cms
-- -----------------------------------------------------
DROP SCHEMA IF EXISTS `roc_cms` ;
CREATE SCHEMA IF NOT EXISTS `roc_cms` DEFAULT CHARACTER SET latin1 ;
USE `roc_cms` ;
-- -----------------------------------------------------
-- Table `roc_cms`.`nodes`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `roc_cms`.`nodes` ;
CREATE TABLE IF NOT EXISTS `roc_cms`.`nodes` (
`nid` INT(11) NOT NULL AUTO_INCREMENT,
`version` INT(11) NULL DEFAULT NULL,
`type` INT(11) NULL DEFAULT NULL,
`title` VARCHAR(255) NOT NULL,
`summary` TEXT NOT NULL,
`content` MEDIUMTEXT NOT NULL,
`author` INT(11) NULL DEFAULT NULL,
`publish` DATETIME NULL DEFAULT NULL,
`created` DATETIME NOT NULL,
`changed` DATETIME NOT NULL,
PRIMARY KEY (`nid`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `roc_cms`.`users`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `roc_cms`.`users` ;
CREATE TABLE IF NOT EXISTS `roc_cms`.`users` (
`uid` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`status` INT(11) NULL DEFAULT NULL,
`created` DATETIME NOT NULL,
`signed` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE INDEX `name` (`name` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `roc_cms`.`users_roles`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `roc_cms`.`users_roles` ;
CREATE TABLE IF NOT EXISTS `roc_cms`.`users_roles` (
`rid` INT(11) NOT NULL AUTO_INCREMENT,
`role` VARCHAR(100) NOT NULL,
PRIMARY KEY (`rid`),
UNIQUE INDEX `role` (`role` ASC))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

View File

@@ -1,14 +0,0 @@
DROP TABLE IF EXISTS nodes;
CREATE TABLE nodes
(
id smallint unsigned NOT NULL auto_increment,
publication_date date NOT NULL, #When the article was published
creation_date date NOT NULL, #When the article was created
modification_date date NOT NULL, #When the article was updated
title varchar(255) NOT NULL, #Full title of the article
summary text NOT NULL, #A short summary of the articule
content mediumtext NOT NULL, #The HTML content of the article
PRIMARY KEY (ID)
);

View File

@@ -1,8 +0,0 @@
DELIMITER $$
CREATE TRIGGER update_editor
AFTER INSERT ON `users_nodes` FOR EACH ROW
UPDATE Nodes
SET editor_id = NEW.users_id
WHERE id = NEW.nodes_id;
$$
DELIMITER ;

View File

@@ -1,100 +0,0 @@
note
description: "Summary description for {ROLE_TEST_SET}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ROLE_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_roles_empty
do
assert ("Not elements",role_provider.roles.after)
assert ("Count = 0", role_provider.count = 0)
end
test_roles_by_id_not_exist
do
assert ("Void", role_provider.role (1) = Void)
end
test_roles_by_name_not_exist
do
assert ("Void", role_provider.role_by_name ("admin") = Void)
end
test_new_role
do
assert ("Count = 0", role_provider.count = 0)
role_provider.new_role ("admin")
assert ("Count = 1", role_provider.count = 1)
assert ("Expected role", attached role_provider.role (1) as l_role and then l_role.name ~ "admin")
assert ("Expected role", attached role_provider.role_by_name ("admin") as l_role and then l_role.id = 1)
end
test_permissions_empty_not_exist_role
do
assert ("Not elements",role_provider.permission_by_role (1).after)
end
test_permissions_empty_exist_role
do
assert ("Count = 0", role_provider.count = 0)
role_provider.new_role ("admin")
assert ("Count = 1", role_provider.count = 1)
assert ("Exist role",not role_provider.roles.after)
assert ("Not permission by role 1 elements",role_provider.permission_by_role (1).after)
end
test_new_role_with_permissions
do
assert ("Count = 0", role_provider.count = 0)
role_provider.new_role ("admin")
role_provider.save_role_permission (1, "Create Page")
role_provider.save_role_permission (1, "Edit Page")
role_provider.save_role_permission (1, "Delete Page")
assert ("Count = 1", role_provider.count = 1)
assert ("Exist role",not role_provider.roles.after)
assert ("Exist role permissions",not role_provider.permission_by_role (1).after)
assert ("Not Exist role permissions, for id 2",role_provider.permission_by_role (2).after)
end
feature {NONE} -- Implementation
role_provider: ROLE_DATA_PROVIDER
-- role provider.
once
create Result.make (connection)
end
end

View File

@@ -1,10 +1,10 @@
note
description: "Summary description for {CMS_STORAGE_MYSQL}."
description: "Summary description for {CMS_STORAGE_STORE_MYSQL}."
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
class
CMS_STORAGE_MYSQL
CMS_STORAGE_STORE_MYSQL
inherit
CMS_STORAGE_STORE_SQL
@@ -26,4 +26,12 @@ feature -- Status report
Result := has_user
end
feature -- Conversion
sql_statement (a_statement: STRING): STRING
-- <Precursor>
do
Result := a_statement
end
end

View File

@@ -1,13 +1,13 @@
note
description: "[
Interface responsible to instantiate CMS_STORAGE_MYSQL object.
Interface responsible to instantiate CMS_STORAGE_STORE_MYSQL object.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_STORAGE_MYSQL_BUILDER
CMS_STORAGE_STORE_MYSQL_BUILDER
inherit
CMS_STORAGE_STORE_SQL_BUILDER
@@ -26,7 +26,7 @@ feature {NONE} -- Initialization
feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_MYSQL
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_STORE_MYSQL
local
conn: DATABASE_CONNECTION
do

View File

@@ -14,66 +14,24 @@ inherit
end
create
make, make_common, make_basic, login_with_connection_string, login_with_schema
login, login_with_default, login_with_database_name, login_with_connection_string, login_with_schema
feature -- Initialization
make_common
login_with_default
-- Create a database handler for MYSQL with common settings.
local
l_retried: BOOLEAN
do
create database_error_handler.make
create db_application.login (username, password)
if not l_retried then
db_application.set_hostname (hostname)
db_application.set_data_source (database_name)
db_application.set_base
create db_control.make
keep_connection := is_keep_connection
if keep_connection then
connect
end
else
create db_control.make
end
rescue
create database_error_handler.make
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
l_retried := True
retry
login_with_database_name (default_database_name)
end
make_basic (a_database_name: STRING)
login_with_database_name (a_database_name: STRING)
-- Create a database handler and
-- set database_name to `a_database_name'.
local
l_retried: BOOLEAN
do
create database_error_handler.make
create db_application.login (username, password)
if not l_retried then
db_application.set_hostname (hostname)
db_application.set_data_source (a_database_name)
db_application.set_base
create db_control.make
keep_connection := is_keep_connection
if keep_connection then
connect
end
else
create db_control.make
end
rescue
create database_error_handler.make
exception_as_error ((create {EXCEPTION_MANAGER}).last_exception)
l_retried := True
retry
login (default_username, default_password, default_hostname, a_database_name, is_keep_connection)
end
make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN)
login (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN)
-- Create a database handler for ODBC and set `username' to `a_username',
-- `password' to `a_password'
@@ -95,24 +53,21 @@ feature -- Initialization
login_with_connection_string (a_string: STRING)
-- Login with `a_connection_string' and immediately connect to database.
local
l_string: LIST[STRING]
l_server: STRING
l_port: STRING
l_schema: STRING
l_database: STRING
l_user: STRING
l_password: STRING
do
create database_error_handler.make
l_string := a_string.split (';')
l_server := l_string.at (2).split ('=').at (2)
l_port := l_string.at (3).split ('=').at (2)
l_schema := l_string.at (4).split ('=').at (2)
l_user := l_string.at (5).split ('=').at (2)
l_password := l_string.at (6).split ('=').at (2)
l_server := connection_string_item (a_string, "Server", default_hostname)
l_database := connection_string_item (a_string, "Database", default_database_name)
l_port := connection_string_item (a_string, "Port", "3306")
l_user := connection_string_item (a_string, "Uid", default_username)
l_password := connection_string_item (a_string, "Pwd", default_password)
create db_application
db_application.set_application (l_schema)
db_application.set_application (l_database)
db_application.set_hostname (l_server + ":" + l_port)
db_application.login_and_connect (l_user, l_password)
db_application.set_base
@@ -120,6 +75,26 @@ feature -- Initialization
keep_connection := is_keep_connection
end
connection_string_item (a_connection_string: STRING; k: STRING; dft: STRING): STRING
local
i,j: INTEGER
do
i := a_connection_string.substring_index (k + "=", 1)
if i = 0 then
i := a_connection_string.substring_index (k.as_lower + "=", 1)
end
if i > 0 then
i := i + k.count + 1
j := a_connection_string.index_of (';', i)
if j = 0 then
j := a_connection_string.count + 1
end
Result := a_connection_string.substring (i, j - 1)
else
Result := dft
end
end
login_with_schema (a_schema: STRING; a_username: STRING; a_password: STRING)
-- Login with `a_connection_string'and immediately connect to database.
do

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="persistence_mysql" uuid="DC757CBD-D8C4-44D6-A07F-C1148D8D233E" library_target="persistence_mysql">
<target name="persistence_mysql">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="store_mysql" uuid="DC757CBD-D8C4-44D6-A07F-C1148D8D233E" library_target="store_mysql">
<target name="store_mysql">
<root all_classes="true"/>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
@@ -24,7 +24,7 @@
<exclude>/database/database_connection_odbc.e</exclude>
</file_rule>
</cluster>
<cluster name="persistence_mysql" location=".\src\" recursive="true">
<cluster name="persistence_store_mysql" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>

View File

@@ -0,0 +1,82 @@
note
description: "Summary description for {CMS_STORAGE_STORE_ODBC}."
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
class
CMS_STORAGE_STORE_ODBC
inherit
CMS_STORAGE_STORE_SQL
redefine
make
end
CMS_CORE_STORAGE_SQL_I
CMS_USER_STORAGE_SQL_I
REFACTORING_HELPER
create
make,
make_with_driver
feature {NONE} -- Initialization
make (a_connection: DATABASE_CONNECTION)
-- <Precursor>
do
Precursor (a_connection)
create odbc_driver.make_from_string_general ("odbc")
end
make_with_driver (a_connection: DATABASE_CONNECTION; a_driver: detachable READABLE_STRING_GENERAL)
require
is_connected: a_connection.is_connected
do
make (a_connection)
if a_driver /= Void then
create odbc_driver.make_from_string_general (a_driver)
end
end
feature -- Status report
odbc_driver: IMMUTABLE_STRING_32
-- Database's driver name.
-- sqlite, mysql, ...
is_initialized: BOOLEAN
-- Is storage initialized?
do
Result := has_user
end
feature -- Conversion
sql_statement (a_statement: STRING): STRING
-- <Precursor>.
local
i: INTEGER
do
Result := a_statement
if odbc_driver.same_caseless_characters_general ("sqlite3", 1, 5, 1) then
from
i := 1
until
i = 0
loop
i := a_statement.substring_index ("AUTO_INCREMENT", i)
if i > 0 then
if Result = a_statement then
create Result.make_from_string (a_statement)
end
Result.remove (i + 4)
i := i + 14
end
end
end
end
end

View File

@@ -0,0 +1,68 @@
note
description: "[
Interface responsible to instantiate CMS_STORAGE_STORE_ODBC object.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
CMS_STORAGE_STORE_ODBC_BUILDER
inherit
CMS_STORAGE_STORE_SQL_BUILDER
GLOBAL_SETTINGS
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
end
feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_STORE_ODBC
local
s: detachable STRING
conn: detachable DATABASE_CONNECTION
do
if
attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.application_config_path) as l_database_config
then
if l_database_config.driver.is_case_insensitive_equal ("odbc") then
s := l_database_config.database_string
if attached reuseable_connection.item as d then
if s.same_string (d.name) then
conn := d.connection
end
end
if conn = Void or else not conn.is_connected then
create {DATABASE_CONNECTION_ODBC} conn.login_with_connection_string (s)
reuseable_connection.replace ([s, conn])
end
if conn.is_connected then
create Result.make_with_driver (conn, l_database_config.item ("Driver"))
set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default.
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)
end
end
end
else
-- Wrong mapping between storage name and storage builder!
end
end
end
reuseable_connection: CELL [detachable TUPLE [name: STRING; connection: DATABASE_CONNECTION]]
once
create Result.put (Void)
end
end

View File

@@ -1,30 +1,26 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="persistence_mysql" uuid="DC757CBD-D8C4-44D6-A07F-C1148D8D233E" library_target="persistence_mysql">
<target name="persistence_mysql">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="persistence_store_odbc" uuid="8FD9D3B3-5FC1-495F-A05D-0205EC966841" library_target="persistence_store_odbc">
<target name="persistence_store_odbc">
<root all_classes="true"/>
<option warning="true" void_safety="none">
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\..\cms.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="layout" location="..\..\layout\layout.ecf"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging.ecf"/>
<library name="model" location="..\..\model\cms_model.ecf"/>
<library name="mysql" location="$ISE_LIBRARY\library\store\dbms\rdbms\mysql\mysql.ecf"/>
<library name="store" location="$ISE_LIBRARY\library\store\store.ecf" readonly="false"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="common" location="..\implementation\store\" recursive="true">
<file_rule>
<exclude>/database/database_connection_odbc.e</exclude>
</file_rule>
</cluster>
<cluster name="persistence_mysql" location=".\src\" recursive="true">
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\..\app_env\app_env-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="model" location="..\..\model\cms_model-safe.ecf"/>
<library name="odbc" location="$ISE_LIBRARY\library\store\dbms\rdbms\odbc\odbc-safe.ecf"/>
<library name="store" location="$ISE_LIBRARY\library\store\store-safe.ecf" readonly="false"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="common" location="..\implementation\store\" recursive="true"/>
<cluster name="persistence_store_odbc" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>

View File

@@ -0,0 +1,26 @@
SQLite ODBC.
Install the odbc driver from http://www.ch-werner.de/sqliteodbc/
Current version
sqliteodbc.exe -- Win32
sqliteodbc_w64.exe -- Win64
Test the ODBC driver using Firefox SQLite DBManager https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/
1. Open the odbc manger from Windows, create a new database using the SQLite3 ODBC driver and then open it from Firefox.
EiffelStore + SQLiteODBC.
Connection String: https://www.connectionstrings.com/sqlite3-odbc-driver/
"Driver=SQLite3 ODBC Driver;Database=./cms_lite.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;"
Edit the database location based on your system.

View File

@@ -0,0 +1,29 @@
note
description : "tests application root class"
date : "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision : "$Revision: 96542 $"
class
APPLICATION
inherit
ARGUMENTS
create
make
feature {NONE} -- Initialization
make
-- Run application.
local
storage: CMS_STORAGE_SQLITE
do
-- Change the path.
create connection.login_with_connection_string ("Driver=SQLite3 ODBC Driver;Database=./cms_lite.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;")
create storage.make (connection)
end
connection: DATABASE_CONNECTION_ODBC
end

Binary file not shown.

View File

@@ -0,0 +1,205 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
testing: "type/manual"
class
NODE_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)
assert ("Empty Nodes", storage.nodes.after)
end
on_clean
-- <Precursor>
do
end
feature -- Test routines
test_new_node
do
assert ("Empty Nodes", storage.nodes.after)
storage.new_node (default_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached storage.node_by_id (1))
-- Not exist node with id 2
assert ("Not exist node with id 2", storage.node_by_id (2) = Void)
end
test_update_node
local
l_node: CMS_NODE
do
assert ("Empty Nodes", storage.nodes.after)
l_node := custom_node ("<h1> test node udpate </h1>", "Update node", "Test case update")
storage.new_node (l_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title )
-- Update node (content and summary)
if attached {CMS_NODE} storage.node_by_id (1) as l_un then
l_un.set_content ("<h1>Updating test node udpate </h1>")
l_un.set_summary ("updating summary")
storage.update_node (l_un)
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then ll_node.title ~ l_node.title )
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title )
end
-- Update node (content and summary and title)
if attached {CMS_NODE} storage.node_by_id (1) as l_un then
l_un.set_content ("<h1>Updating test node udpate </h1>")
l_un.set_summary ("updating summary")
l_un.set_title ("Updating Test case")
storage.update_node (l_un)
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then not (ll_node.title ~ l_node.title) )
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title )
end
end
test_update_title
local
l_node: CMS_NODE
do
assert ("Empty Nodes", storage.nodes.after)
l_node := custom_node ("<h1> test node udpate </h1>", "Update node", "Test case update")
storage.new_node (l_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title )
-- Update node title
if attached {CMS_NODE} storage.node_by_id (1) as l_un then
storage.update_node_title (1, l_un.id, "New Title")
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then not ( ll_node.title ~ l_un.title) and then ll_node.title ~ "New Title" )
end
end
test_update_summary
local
l_node: CMS_NODE
do
assert ("Empty Nodes", storage.nodes.after)
l_node := custom_node ("<h1> test node udpate </h1>", "Update node", "Test case update")
storage.new_node (l_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title )
-- Update node summary
if attached {CMS_NODE} storage.node_by_id (1) as l_un then
storage.update_node_summary (1, l_un.id,"New Summary")
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_un.content and then not (ll_node.summary ~ l_un.summary) and then ll_node.summary ~ "New Summary" and then ll_node.title ~ l_un.title)
end
end
test_update_content
local
l_node: CMS_NODE
do
assert ("Empty Nodes", storage.nodes.after)
l_node := custom_node ("<h1> test node udpate </h1>", "Update node", "Test case update")
storage.new_node (l_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title )
-- Update node content
if attached {CMS_NODE} storage.node_by_id (1) as l_un then
storage.update_node_content (1, l_un.id, "New Content")
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then not (ll_node.content ~ l_un.content) and then ll_node.content ~ "New Content" and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title)
end
end
test_delete_node
local
l_node: CMS_NODE
do
assert ("Empty Nodes", storage.nodes.after)
l_node := custom_node ("<h1> test node udpate </h1>", "Update node", "Test case update")
storage.new_node (l_node)
assert ("Not empty Nodes after new_node", not storage.nodes.after)
-- Exist node with id 1
assert ("Exist node with id 1", attached {CMS_NODE} storage.node_by_id (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title )
-- Delte node 1
storage.delete_node_by_id (1)
assert ("Node does not exist", storage.node_by_id (1) = Void)
end
test_recent_nodes
-- Content_10, Summary_10, Title_10
-- Content_9, Summary_9, Title_9
-- ..
-- Content_1, Summary_1, Title_1
local
i : INTEGER
do
assert ("Empty Nodes", storage.nodes.after)
across 1 |..| 10 as c loop
storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
end
-- Scenario (0,10) rows, recents (10 down to 1)
i := 10
across storage.recent_nodes (0, 10) as c loop
assert ("Same id:" + i.out, c.item.id = i)
i := i - 1
end
-- Scenario (5, 10) rows, recent nodes (5 down to 1)
i := 5
across storage.recent_nodes (5, 10) as c loop
assert ("Same id:" + i.out, c.item.id = i)
i := i - 1
end
-- Scenario (9,10) rows, recent node 1
i := 1
across storage.recent_nodes (9, 10) as c loop
assert ("Same id:" + i.out, c.item.id = i)
i := i - 1
end
-- Scenrario 10..10 empty
assert ("Empty", storage.recent_nodes (10, 10).after)
end
end

View File

@@ -0,0 +1,273 @@
note
description: "Summary description for {STORAGE_TEST_SET}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
STORAGE_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_has_user
do
assert ("Not has user", not storage.has_user)
end
test_all_users
do
assert ("to implement all_users", False)
end
test_user_by_id_not_exist
do
assert ("User does not exist", storage.user_by_id (1) = Void)
end
test_user_by_name_not_exist
do
assert ("User does not exist", storage.user_by_name ("test") = Void)
end
test_user_by_email_not_exist
do
assert ("User does not exist", storage.user_by_name ("test@test.com") = Void)
end
test_user_with_bad_id
local
l_retry: BOOLEAN
l_user: detachable CMS_USER
do
if not l_retry then
l_user := storage.user_by_id (0)
assert ("Precondition does not get the wrong value", False)
else
assert ("Expected precondition violation", True)
end
rescue
l_retry := True
retry
end
test_user_with_bad_name_empty
local
l_retry: BOOLEAN
l_user: detachable CMS_USER
do
if not l_retry then
l_user := storage.user_by_name ("")
assert ("Precondition does not get the wrong value", False)
else
assert ("Expected precondition violation", True)
end
rescue
l_retry := True
retry
end
test_user_with_bad_email_empty
local
l_retry: BOOLEAN
l_user: detachable CMS_USER
do
if not l_retry then
l_user := storage.user_by_email ("")
assert ("Precondition does not get the wrong value", False)
else
assert ("Expected precondition violation", True)
end
rescue
l_retry := True
retry
end
test_save_user
do
storage.save_user (default_user)
assert ("Has user", storage.has_user)
end
test_user_by_id
do
storage.save_user (default_user)
assert ("Has user", storage.has_user)
if attached {CMS_USER} storage.user_by_id (1) as l_user then
assert ("Exist", True)
assert ("User test", l_user.name ~ "test")
assert ("User id = 1", l_user.id = 1)
else
assert ("Wrong Implementation", False)
end
end
test_user_by_name
do
storage.save_user (default_user)
assert ("Has user", storage.has_user)
if attached {CMS_USER} storage.user_by_name ("test") as l_user then
assert ("Exist", True)
assert ("User nane: test", l_user.name ~ "test")
else
assert ("Wrong Implementation", False)
end
end
test_user_by_email
do
storage.save_user (default_user)
assert ("Has user", storage.has_user)
if attached {CMS_USER} storage.user_by_email ("test@test.com") as l_user then
assert ("Exist", True)
assert ("User email: test@test.com", l_user.email ~ "test@test.com")
else
assert ("Wrong Implementation", False)
end
end
test_invalid_credential
do
storage.save_user (default_user)
assert ("Has user test", attached storage.user_by_name ("test"))
assert ("Wrong password", not storage.is_valid_credential ("test", "test"))
assert ("Wrong user", not storage.is_valid_credential ("test1", "test"))
end
test_valid_credential
do
storage.save_user (default_user)
assert ("Has user test", attached storage.user_by_name ("test"))
assert ("Valid password", storage.is_valid_credential ("test", "password"))
end
-- test_recent_nodes_empty
-- do
-- assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty)
-- end
-- test_recent_nodes
-- local
-- l_nodes: LIST[CMS_NODE]
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- l_nodes := storage.recent_nodes (0, 10)
-- assert ("10 recent nodes", l_nodes.count = 10)
-- assert ("First node id=10", l_nodes.first.id = 10)
-- assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (5, 10)
-- assert ("5 recent nodes", l_nodes.count = 5)
-- assert ("First node id=5", l_nodes.first.id = 5)
-- assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (9, 10)
-- assert ("1 recent nodes", l_nodes.count = 1)
-- assert ("First node id=1", l_nodes.first.id = 1)
-- assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (10, 10)
-- assert ("Is empty", l_nodes.is_empty)
-- end
-- test_node_does_not_exist
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("Not exist node id: 12", storage.node_by_id (12) = Void)
-- end
-- test_node
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("has nodes", storage.nodes.count > 5)
-- assert ("Node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
-- end
-- test_update_node
-- local
-- l_node: CMS_NODE
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- l_node := ll_node.twin
-- l_node.set_content ("New Content")
-- l_node.set_summary ("New Summary")
-- l_node.set_title("New Title")
---- storage.update_node (l_node)
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
-- test_update_node_title
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_title (ll_node.id, "New Title")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
-- end
-- end
-- test_update_node_summary
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_summary (ll_node.id, "New Summary")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
-- test_update_node_content
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_content (ll_node.id, "New Content")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
-- end
-- end
-- test_delete_node
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("Exist node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
-- storage.delete_node_by_id (10)
-- assert ("Not exist node id: 10", storage.node_by_id (10) = Void)
-- end
end

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="tests" uuid="FE27C81D-3F7D-4E46-992B-55F4BBDA4F8B">
<target name="tests">
<root class="APPLICATION" feature="make"/>
<option warning="true" void_safety="conformance">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\..\..\cms-safe.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="model" location="..\..\..\model\cms_model-safe.ecf"/>
<library name="module_node" location="..\..\..\..\modules\node\node-safe.ecf"/>
<library name="persitence_sqlite" location="..\persistence_sqlite-safe.ecf" readonly="false"/>
<library name="process" location="$ISE_LIBRARY\library\process\process-safe.ecf"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<cluster name="tests" location=".\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
<exclude>/nodes$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,72 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
testing: "type/manual"
class
USER_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)
storage.new_user (custom_user ("admin", "admin","admin@admin.com"))
end
on_clean
-- <Precursor>
do
end
feature -- Test routines
test_user_exist
-- User admin exist
do
assert ("Not void", attached storage.user_by_email ("admin@admin.com"))
assert ("Not void", attached storage.user_by_id (1))
assert ("Not void", attached storage.user_by_name ("admin"))
end
test_user_not_exist
-- Uset test does not exist.
do
assert ("Void", storage.user_by_email ("test@admin.com") = Void)
assert ("Void", storage.user_by_id (2) = Void )
assert ("Void", storage.user_by_name ("test") = Void)
end
test_new_user
do
storage.new_user (custom_user ("test", "test","test@admin.com"))
assert ("Not void", attached storage.user_by_email ("test@admin.com"))
assert ("Not void", attached storage.user_by_id (2))
assert ("Not void", attached storage.user_by_id (2) as l_user and then l_user.id = 2 and then l_user.name ~ "test")
assert ("Not void", attached storage.user_by_name ("test"))
end
end

View File

@@ -0,0 +1,55 @@
note
description: "Summary description for {ABSTRACT_DB_TEST}."
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
ABSTRACT_DB_TEST
feature -- Database connection
connection: DATABASE_CONNECTION_ODBC
-- odbc database connection
once
create Result.login_with_connection_string ("Driver=SQLite3 ODBC Driver;Database=cms_lite.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;")
-- create Result.make_basic ("cms_lite.db")
end
feature {NONE} -- Implementation
storage: CMS_STORAGE
-- node provider.
once
create {CMS_STORAGE_SQLITE} Result.make (connection)
end
feature {NONE} -- Fixture Factory: Users
default_user: CMS_USER
do
Result := custom_user ("test", "password", "test@test.com")
end
custom_user (a_name, a_password, a_email: READABLE_STRING_32): CMS_USER
do
create Result.make (a_name)
Result.set_password (a_password)
Result.set_email (a_email)
end
--feature {NONE} -- Fixture Factories: Nodes
-- 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_PAGE
-- do
-- create Result.make (a_title)
-- Result.set_content (a_content, a_summary, Void)
-- end
end

View File

@@ -0,0 +1,66 @@
note
description: "[
Setting up database tests
1. Put the database in a known state before running your test suite.
2. Data reinitialization. For testing in developer sandboxes, something that you should do every time you rebuild the system, you may want to forgo dropping and rebuilding the database in favor of simply reinitializing the source data.
You can do this either by erasing all existing data and then inserting the initial data vales back into the database, or you can simple run updates to reset the data values.
The first approach is less risky and may even be faster for large amounts of data. - See more at: http://www.agiledata.org/essays/databaseTesting.html#sthash.6yVp35g8.dpuf
]"
date: "$Date$"
revision: "$Revision$"
EIS: "name=Database Testing", "src=http://www.agiledata.org/essays/databaseTesting.html", "protocol=uri"
class
CLEAN_DB
feature
clean_db (a_connection: DATABASE_CONNECTION)
-- Clean db test.
local
l_parameters: STRING_TABLE[STRING]
do
create l_parameters.make (0)
db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_nodes, l_parameters))
db_handler(a_connection).execute_change
-- Clean Users
db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users, l_parameters))
db_handler(a_connection).execute_change
-- -- Reset Autoincremente
-- db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_users_autoincrement, l_parameters))
-- db_handler(a_connection).execute_change
-- db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_nodes_autoincrement, l_parameters))
-- db_handler(a_connection).execute_change
end
feature -- Database Hanlder
db_handler (a_connection: DATABASE_CONNECTION): DATABASE_HANDLER
-- Db handler
once
create {DATABASE_HANDLER_IMPL} Result.make (a_connection)
end
feature -- Sql delete queries
Sql_delete_users: STRING = "delete from Users"
-- Clean Users.
Sql_delete_nodes: STRING = "delete from Nodes"
-- Clean Nodes.
Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1"
-- reset autoincrement
Rest_nodes_autoincrement: STRING = "ALTER TABLE Nodes AUTO_INCREMENT = 1"
-- reset autoincrement.
end

View File

@@ -109,4 +109,12 @@ feature -- Access
Result:= sql_storage.sql_item (a_index)
end
feature -- Conversion
sql_statement (a_statement: STRING): STRING
-- <Precursor>
do
Result := sql_storage.sql_statement (a_statement)
end
end

View File

@@ -150,22 +150,42 @@ feature -- Helper
sql_execute_script (a_sql_script: STRING)
-- Execute SQL script.
-- i.e: multiple SQL statements.
local
i: INTEGER
err: BOOLEAN
do
reset_error
-- sql_begin_transaction
sql_change (a_sql_script, Void)
-- sql_commit_transaction
sql_begin_transaction
-- Issue on MySQL with multiple statements
-- sql_change (a_sql_script, Void)
from
i := 1
until
i > a_sql_script.count or err
loop
if attached next_sql_statement (a_sql_script, i) as s then
if not s.is_whitespace then
sql_change (sql_statement (s), Void)
err := err or has_error
reset_error
end
i := i + s.count
else
i := a_sql_script.count + 1
end
end
if err then
sql_rollback_transaction
else
sql_commit_transaction
end
end
sql_table_exists (a_table_name: READABLE_STRING_8): BOOLEAN
-- Does table `a_table_name' exists?
local
l_params: STRING_TABLE [detachable ANY]
do
reset_error
create l_params.make (1)
l_params.force (a_table_name, "tbname")
sql_query ("SELECT count(*) FROM :tbname ;", l_params)
sql_query ("SELECT count(*) FROM " + a_table_name + " ;", Void)
Result := not has_error
-- FIXME: find better solution
reset_error
@@ -173,13 +193,9 @@ feature -- Helper
sql_table_items_count (a_table_name: READABLE_STRING_8): INTEGER_64
-- Number of items in table `a_table_name'?
local
l_params: STRING_TABLE [detachable ANY]
do
reset_error
create l_params.make (1)
l_params.force (a_table_name, "tbname")
sql_query ("SELECT count(*) FROM :tbname ;", l_params)
sql_query ("SELECT count(*) FROM " + a_table_name + " ;", Void)
if not has_error then
Result := sql_read_integer_64 (1)
end
@@ -325,4 +341,57 @@ feature -- Access
end
end
feature -- Conversion
sql_statement (a_statement: STRING): STRING
-- Statement normalized for underlying SQL database.
deferred
end
feature {NONE} -- Implementation
next_sql_statement (a_script: STRING; a_start_index: INTEGER): detachable STRING
local
i,j,n: INTEGER
c: CHARACTER
l_end: INTEGER
do
from
i := a_start_index
n := a_script.count
until
i > n or a_script[i] = ';'
loop
c := a_script[i]
inspect c
when '`', '"', '%'' then
from
j := i
l_end := 0
until
l_end > 0 or j > n
loop
j := a_script.index_of (c, i + 1)
if j > i then
if a_script [j - 1] /= '\' then
l_end := j
end
end
end
if l_end > 0 then
i := l_end
else
i := j
end
else
end
i := i + 1
end
i := a_script.index_of (';', a_start_index)
if i > a_start_index then
Result := a_script.substring (a_start_index, i)
end
end
end