You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This artifact objective is to illustrate the entities and relations between them that are relevant in our project context.
For that, UML was used since it's common among the field and it's easy to understand.
This conceptual model will be used as a support to design the system's database.
1. Class diagram
2. Additional Business Rules
BR06. A user can only send a message to a chat he has access to.
BR07. A ban cannot be applied to an administrator.
A5: Relational Schema, validation and schema refinement
This artifact contains the Relational Schema obtained by mapping from the Conceptual Data Model. The Relational Schema includes each relation schema, attributes, domains, primary keys, foreign keys and other integrity rules: UNIQUE (UK), DEFAULT (DF), NOT NULL (NN), CHECK (CK). A Schema Validation section was added to verify the normal form of each table. An Annex containing the SQL code to create de schema.
Relational Schema
Relation reference
Relation Compact Notation
R01
users(id, username UK NN, password NN, name NN, birth NN CK birth < Today, email UK NN, phone_number)
R02
project(id, title NN, description, creation NN CK creation <= Today, is_archived NN DF false, id_coordinator->users NN)
R03
board(id, name NN, id_project->project NN)
R04
column(id, name NN, id_board->board NN)
R05
task(id, name NN, description, creation_date NN CK creation_date <= Today ,due_date CK creation_date < due_date, id_column->column NN, id_chat->chat)
R06
label(id, name NN, 1<= color NN <=16777215 )
R07
label_class(id, name NN)
R08
forum(id_project->project)
R09
post(id, name NN, id_forum->forum NN)
R10
message(id, message NN, sent_date NN CK sent_date <= Today, id_users->users NN, id_chat->chat NN)
R11
role(id, name NN, id_project->project NN)
R12
permission(id, name NN)
R13
administrator(id, id_users->users NN)
R14
ban(id, start_date NN, end_date NN CK start_date < end_date, reason, id_users -> users NN, id_administrator->administrator NN)
R15
faq(id, question NN UK, answer NN)
R16
notification(id, date NN CK date <= Today, message NN)
comment(id, msg NN, sent_date NN CK sent_date <= Today, id_task->task NN, id_users->users NN)
Legend:
UK = UNIQUE KEY
NN = NOT NULL
DF = DEFAULT
CK = CHECK
There are only two generalisations. One of them is in the User, an Administrator is specific kind of User that has more power.
Other generalisation is in the Notifications we have 4 kinds of notifications: New Message (a user receives a notification when they receive a new message), New Coordinator (a user is notified when some of the projects he is enrolled in changed coordinator), Task Moved (a user is notified when a task is moved from one column to another ) and New Assign (when the user is assigned to a task).
Because all relations are in the Boyce–Codd Normal Form (BCNF), the relational schema is also in the BCNF and, therefore, the schema does not need to be further normalized.
A6: Indexes, triggers, transactions and database population
This artifact contains the physical schema of the database, the identification and characterisation of the indexes, the support of data integrity rules with triggers and the definition of the database user-defined functions.
Furthermore, it also shows the database transactions needed to assure the integrity of the data in the presence of concurrent accesses. For each transaction, the isolation level is explicitly stated and justified.
This artifact also contains the database's workload as well as the complete database creation script, including all SQL necessary to define all integrity constraints, indexes and triggers. Finally, this artifact also includes a separate script with INSERT statements to populate the database.
1. Database Workload
Relation
Relation name
Order of magnitude
Estimated growth
R01
users
10 k (tens of thousands)
10 (tens) / day
R02
project
1 k (thousands)
1 (units) / day
R03
board
1 k
1 / day
R04
column
10 k
10 / day
R05
task
100 k (hundreds of thousands)
100 (hundreds) / day
R06
label
10 k
1 / day
R07
label_class
1 k
1 / day
R08
forum
1 k
1 / day
R09
chat
100 k
100 / day
R10
message
1 kk (millions)
1 k / day
R11
role
1 k
1 / day
R12
permission
10
1 / month
R13
administrator
10
1 / month
R14
ban
100
1 / day
R15
faq
1
1 / month
R16
notification
1 kk
1 k / day
R17
new_message
1 kk
1 k / day
R18
new_coordinator
100
1 / day
R19
new_assign
100 k
100 / day
R20
task_moved
100 k
100 / day
R21
assignment
100 k
100 / day
R22
notified
1 kk
1 k / day
R23
users_role
10 k
1 / day
R24
collaborator
10 k
10 / day
R25
label_label_class
10 k
10 / day
R26
label_task
100 k
10 / day
R27
role_permission
1 k
1 / day
2. Indexes
2.1 Performance indexes
Index
IDX01
Index relation
collaborator
Index attribute
id_users
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the user’s projects will be a frequent enough operation, a performance index for the collaborator table on the id_users attribute would be useful. The cardinality is medium since there may be repeated id_users values. Sorting id_users is also not required so a hashmap was chosen. Clustering was added because fetching all the user’s projects will be needed and the cardinality is medium.
SQL Code
CREATEINDEXuser_projects_indexON collaborator USING hash (id_users);
Index
IDX02
Index relation
comment
Index attribute
id_task
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the task’s comments will be a frequent enough operation, a performance index for the comment table on the id_task attribute would be useful. The cardinality is medium since there may be repeated id_task values. Sorting id_task is also not required so a hashmap was chosen. Clustering was added because fetching all the task’s comments will be needed and the cardinality is medium.
SQL Code
CREATEINDEXtask_comments_indexON comment USING hash (id_task);
Index
IDX03
Index relation
task
Index attribute
id_vertical
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the task’s columns will be a frequent enough operation, a performance index for the task table on the id_task attribute would be useful. The cardinality is medium since there may be repeated id_vertical values. Sorting id_vertical is also not required so a hashmap was chosen. Clustering was added because fetching all the task’s columns will be needed and the cardinality is medium.
SQL Code
CREATEINDEXcolumn_tasks_indexON task USING hash (id_vertical);
Index
IDX04
Index relation
vertical
Index attribute
id_board
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the board’s columns will be a frequent enough operation, a performance index for the vertical table on the id_board attribute would be useful. The cardinality is medium since there may be repeated id_board values. Sorting id_board is also not required so a hashmap was chosen. Clustering was added because fetching all the board’s columns will be needed and the cardinality is medium.
SQL Code
CREATEINDEXboard_verticals_indexON vertical USING hash (id_board);
Index
IDX05
Index relation
board
Index attribute
id_project
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the project’s boards will be a frequent enough operation, a performance index for the board table on the id_project attribute would be useful. The cardinality is medium since there may be repeated id_project values. Sorting id_project is also not required so a hashmap was chosen. Clustering was added because fetching all the project’s boards will be needed and the cardinality is medium.
SQL Code
CREATEINDEXproject_boards_indexON board USING hash (id_project);
Index
IDX06
Index relation
label_task
Index attribute
id_task
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the task’s labels will be a frequent enough operation, a performance index for the label_task table on the id_task attribute would be useful. The cardinality is medium since there may be repeated id_task values. Sorting id_task is also not required so a hashmap was chosen. Clustering was added because fetching all the task’s labels will be needed and the cardinality is medium.
SQL Code
CREATEINDEXtask_labels_indexON label_task USING hash (id_task);
Index
IDX07
Index relation
notified
Index attribute
id_users
Index type
Hash
Cardinality
Medium
Clustering
Yes
Justification
Since fetching the user’s notifications will be a frequent enough operation, a performance index for the notified table on the id_users attribute would be useful. The cardinality is medium since there may be repeated id_users values. Sorting id_users is also not required so a hashmap was chosen. Clustering was added because fetching all the user’s notifications will be needed and the cardinality is medium.
SQL Code
CREATEINDEXuser_notifications_indexON notified USING hash (id_users);
Index
IDX08
Index relation
notification
Index attribute
sent_date
Index type
Btree
Cardinality
Medium
Clustering
No
Justification
We predict the use of date filtering on notifications, i.e to see the most recent notifications. So a performance index for the notification table on the sent_date attribute using a btree was implemented.
SQL Code
CREATEINDEXnotifications_date_indexON notification USING btree (sent_date);
2.2 Full-text Search indexes
Index
INDX09
Index Relation
Users
Index type
GIN
Clustering
No
Justification
To provide our users a way of searching by other users with more weight on usernames then on name
SQL Code
ALTERTABLE users
ADD COLUMN tsvectors TSVECTOR;
CREATE OR REPLACEFUNCTIONusers_search_update ()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP ='INSERT' THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.username), 'A') || setweight(to_tsvector('english', NEW.name), 'B'));
END IF;
IF TG_OP ='UPDATE' THEN
IF (NEW.name<>OLD.name) || (NEW.username<>OLD.username) THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.username), 'A') || setweight(to_tsvector('english', NEW.name), 'B'));
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATETRIGGERusers_search_update_trigger
BEFORE INSERT ORUPDATEON users
FOR EACH ROW
EXECUTE PROCEDURE users_search_update ();
DROPINDEX IF EXISTS search_users_idx;
CREATEINDEXsearch_users_idxON users USING GIN (tsvectors);
Index
INDX10
Index Relation
Projects
Index type
GIN
Clustering
No
Justification
To provide our users a way of searching by projects based on project titles and its descrition
SQL Code
ALTERTABLE project
ADD COLUMN tsvectors TSVECTOR;
CREATE OR REPLACEFUNCTIONproject_search_update ()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP ='INSERT' THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.title), 'A') || setweight(to_tsvector('english', NEW.description), 'B'));
END IF;
IF TG_OP ='UPDATE' THEN
IF (NEW.title<>OLD.title) OR (NEW.description<>OLD.description) THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.title), 'A') || setweight(to_tsvector('english', NEW.description), 'B'));
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATETRIGGERproject_search_update_trigger
BEFORE INSERT ORUPDATEON project
FOR EACH ROW
EXECUTE PROCEDURE project_search_update ();
DROPINDEX IF EXISTS search_project_idx;
CREATEINDEXsearch_project_idxON project USING GIN (tsvectors);
Index
INDX11
Index Relation
Tasks
Index type
GIN
Clustering
No
Justification
To provide our users a way of searching by tasks based on the taks's name and description with enphasis on the name
SQL Code
ALTERTABLE task
ADD COLUMN tsvectors TSVECTOR;
CREATE OR REPLACEFUNCTIONtask_search_update ()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP ='INSERT' THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.name), 'A') || setweight(to_tsvector('english', coalesce(NEW.description, '')), 'B'));
END IF;
IF TG_OP ='UPDATE' THEN
IF (NEW.name<>OLD.name) OR (NEW.description<>OLD.description) THEN
NEW.tsvectors= (setweight(to_tsvector('english', NEW.name), 'A') || setweight(to_tsvector('english', coalesce(NEW.description, '')), 'B'));
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATETRIGGERtask_search_update_trigger
BEFORE INSERT ORUPDATEON task
FOR EACH ROW
EXECUTE PROCEDURE task_search_update ();
DROPINDEX IF EXISTS search_task_idx;
CREATEINDEXsearch_task_idxON task USING GIN (tsvectors);
Index
INDX09
Index Relation
label
Index type
GIN
Clustering
No
Justification
To provide our users a way of searching by labels based on their name
SQL Code
ALTERTABLE label
ADD COLUMN tsvectors TSVECTOR;
CREATE OR REPLACEFUNCTIONlabel_search_update ()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP ='INSERT' THEN
NEW.tsvectors= to_tsvector('english', NEW.name);
END IF;
IF TG_OP ='UPDATE' THEN
IF NEW.name<>OLD.name THEN
NEW.tsvectors= to_tsvector('english', NEW.name);
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATETRIGGERlabel_search_update_trigger
BEFORE INSERT ORUPDATEON label
FOR EACH ROW
EXECUTE PROCEDURE label_search_update ();
DROPINDEX IF EXISTS search_label_idx;
CREATEINDEXsearch_label_idxON label USING GIN (tsvectors);
Triggers
Trigger
TRIGGER01
Description
An user can only send a message in a forum he has access to.
SQL Code
CREATEFUNCTIONsend_message() RETURNS TRIGGER AS
$BODY$
BEGIN
IF NOT EXISTS (SELECT*FROM collaborator WHERE id_user =NEW.id_userAND id_project IN (SELECT id_forum AS id_project FROM post WHERE id IN (SELECT id_post AS id FROM msg WHERENEW.id= id))) THEN
RAISE EXCEPTION 'User does not have access to this forum';
END IF;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERsend_message
BEFORE INSERT ORUPDATEON msg
FOR EACH ROW
EXECUTE PROCEDURE send_message();
Trigger
TRIGGER02
Description
A ban cannot be applied to an admninistrator
SQL Code
CREATEFUNCTIONissue_ban() RETURNS TRIGGER AS
$BODY$
BEGIN
IF EXISTS (SELECT*FROM administrator WHERE id_users IN (SELECT id_users AS banned_id FROM ban WHERENEW.id= id)) THEN
RAISE EXCEPTION 'Cannot ban an administrator';
END IF;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERissue_ban
BEFORE INSERT ORUPDATEON ban
FOR EACH ROW
EXECUTE PROCEDURE issue_ban();
Trigger
TRIGGER03
Description
Issue notification on task assignment
SQL Code
CREATEFUNCTIONnotify_assignment() RETURNS TRIGGER AS
$BODY$
DECLARE
id_notf INTEGER;
BEGININSERT INTO notification(sent_date, msg)
VALUES (current_date, 'You have been assigned a new task.') RETURNING id INTO id_notf;
INSERT INTO new_assign(id_notification, id_users, id_task)
VALUES (id_notf, NEW.id_users, NEW.id_task);
INSERT INTO notified(id_users, id\_notification)
VALUES (NEW.id_users, id_notf);
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERnotify_assignment
AFTER INSERT ORUPDATEON assignmnt
FOR EACH ROW
EXECUTE PROCEDURE notify_assignment();
Trigger
TRIGGER04
Description
Issue notification on new coordinator
SQL Code
CREATEFUNCTIONnotify_new_coordinator() RETURNS TRIGGER AS
$BODY$
DECLARE
id_notf INTEGER;
BEGININSERT INTO notification(sent_date, msg)
VALUES (current_date, 'One of your projects has a new coordinator') RETURNING id INTO id_notf;
INSERT INTO new_coordinator(id_notification, id_project)
VALUES (id_noft, NEW.id);
INSERT INTO notified(id_users, id_notification)
SELECT id_users, id AS id_notification FROM collaborator CROSS JOIN notification WHERE id_project =NEW.idAND id = id_notf;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERnotify_new_coordinator
AFTER UPDATEON project
FOR EACH ROW
EXECUTE PROCEDURE notify_new_coordinator();
Trigger
TRIGGER05
Description
Issue notification on moved task
SQL Code
CREATEFUNCTIONnotify_moved_task() RETURNS TRIGGER AS
$BODY$
DECLARE
id_notf INTEGER;
BEGIN
IF NOT EXISTS (SELECT*FROM task WHERE id =NEW.idAND id_vertical =NEW.id_vertical) THEN
INSERT INTO notification(sent_date, msg)
VALUES (current_date, 'A task you are assigned to has been moved') RETURNING id INTO id_notf;
INSERT INTO task_moved(id_notification, id_task)
VALUES (id_noft, NEW.id);
INSERT INTO notified(id_users, id_notification)
SELECT id_users, id AS id_notification FROM assignmnt CROSS JOIN notification WHERE id = id_notf AND id\_task =NEW.id;
END IF;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERnotify_moved_task
BEFORE UPDATEON task
FOR EACH ROW
EXECUTE PROCEDURE notify_moved_task();
Trigger
TRIGGER06
Description
Issue notification on new message
SQL Code
CREATEFUNCTIONnotify_message() RETURNS TRIGGER AS
$BODY$
DECLARE
id_notf INTEGER;
BEGININSERT INTO notification(sent_date, msg)
VALUES (current_date, 'You have received a new message.') RETURNING id INTO id_notf;
INSERT INTO new_message(id_notification, id_message)
VALUES (id_noft, NEW.id);
INSERT INTO notified(id_users, id_notification)
SELECT id_users, id AS id_notification FROM collaborator CROSS JOIN notification WHERE id_project IN (SELECT id\_forum FROM chat WHERE id IN (SELECT id_chat FROM message WHERENEW.id= id)) AND id = id_notf;
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERnotify_message
AFTER INSERT ON msg
FOR EACH ROW
EXECUTE PROCEDURE notify_message();
Trigger
TRIGGER07
Description
Archive project when its coordinator deletes his account
SQL Code
CREATEFUNCTIONarchive_project() RETURNS TRIGGER AS
$BODY$
BEGINUPDATE project
SET is_archived = true
id_coordinator =nullWHERE id IN (SELECT id FROM project WHERE id_coordinator =OLD.id);
RETURN OLD;
END
$BODY$
LANGUAGE plpgsql;
CREATETRIGGERarchive_project
BEFORE DELETEON users
FOR EACH ROW
EXECUTE PROCEDURE archive_project();
Transaction
Transaction
TRAN01
Description
Get comments and their number
Justification
In the middle of the transaction, the insertion of new rows in the comment table can occur, which implies that the information retrieved in both selects is different, consequently resulting in a Phantom Read. It's READ ONLY because it only uses Selects.
Isolation Level
SERIALIZABLE READ ONLY
SQL Code
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY;
\-- Get number of current comments on a taskSELECTCOUNT(\*)
FROM comment
WHERE task\_id=$id;
\-- Get comments on a taskSELECT \*FROM loan
WHERE task\_id=$id;
END TRANSACTION;