Add: DB SQL Setup queries
This commit is contained in:
parent
4d53448465
commit
c45b7dd5b0
16
db/clear.sql
Normal file
16
db/clear.sql
Normal file
@ -0,0 +1,16 @@
|
||||
DELETE FROM doki8902.message_content;
|
||||
DELETE FROM doki8902.comment;
|
||||
DELETE FROM doki8902.post;
|
||||
DELETE FROM doki8902.message_initial;
|
||||
DELETE FROM doki8902.message_vote;
|
||||
DELETE FROM doki8902.message;
|
||||
DELETE FROM doki8902.post_category;
|
||||
DELETE FROM doki8902.user_session;
|
||||
DELETE FROM doki8902.user;
|
||||
DELETE FROM doki8902.user_action_log;
|
||||
|
||||
ALTER SEQUENCE doki8902.user_id_seq RESTART;
|
||||
ALTER SEQUENCE doki8902.user_action_log_id_seq RESTART;
|
||||
ALTER SEQUENCE doki8902.message_id_seq RESTART;
|
||||
ALTER SEQUENCE doki8902.message_content_id_seq RESTART;
|
||||
ALTER SEQUENCE doki8902.post_category_id_seq RESTART;
|
||||
675
db/create.sql
Normal file
675
db/create.sql
Normal file
@ -0,0 +1,675 @@
|
||||
---------------------------
|
||||
---== TYPES ==-------------
|
||||
|
||||
CREATE TYPE doki8902.user_access_type AS ENUM (
|
||||
'normal', 'elevated'
|
||||
);
|
||||
|
||||
CREATE TYPE doki8902.user_action_type AS ENUM (
|
||||
'post_create', 'post_edit', 'post_delete',
|
||||
'comment_create', 'comment_edit', 'comment_delete',
|
||||
'user_join', 'user_restrict'
|
||||
);
|
||||
|
||||
---------------------------
|
||||
---== TABLES ==------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.user (
|
||||
id SERIAL
|
||||
NOT NULL
|
||||
PRIMARY KEY,
|
||||
username VARCHAR(32)
|
||||
NOT NULL,
|
||||
password VARCHAR(200)
|
||||
NOT NULL,
|
||||
join_time TIMESTAMP WITH TIME ZONE
|
||||
NOT NULL
|
||||
DEFAULT CURRENT_TIMESTAMP,
|
||||
about VARCHAR(256)
|
||||
NOT NULL
|
||||
DEFAULT 'Hey! I''m new here!',
|
||||
access doki8902.user_access_type
|
||||
NOT NULL
|
||||
DEFAULT 'normal',
|
||||
|
||||
CONSTRAINT username_valid_symbols CHECK (username ~ '^[a-zA-Z0-9]*$'),
|
||||
CONSTRAINT username_length_min CHECK (char_length(username) >= 3)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.user_session (
|
||||
user_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.user(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
token CHAR(32)
|
||||
NOT NULL
|
||||
PRIMARY KEY
|
||||
DEFAULT md5(random()::text),
|
||||
expiry TIMESTAMP
|
||||
NOT NULL
|
||||
DEFAULT CURRENT_TIMESTAMP + INTERVAL '1 WEEK'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.user_action_log (
|
||||
id SERIAL
|
||||
NOT NULL
|
||||
PRIMARY KEY,
|
||||
user_id INT
|
||||
REFERENCES doki8902.user(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
type doki8902.user_action_type
|
||||
NOT NULL,
|
||||
details VARCHAR(100),
|
||||
timestamp TIMESTAMP WITH TIME ZONE
|
||||
NOT NULL
|
||||
DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.message (
|
||||
id SERIAL
|
||||
NOT NULL
|
||||
PRIMARY KEY,
|
||||
author_id INT
|
||||
REFERENCES doki8902.user(id)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE,
|
||||
created_date TIMESTAMP WITH TIME ZONE
|
||||
NOT NULL
|
||||
DEFAULT CURRENT_TIMESTAMP,
|
||||
soft_delete BOOLEAN
|
||||
NOT NULL
|
||||
DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.message_vote (
|
||||
message_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.message(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
user_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.user(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
vote SMALLINT
|
||||
NOT NULL,
|
||||
|
||||
CONSTRAINT message_user_id_pkey PRIMARY KEY (message_id, user_id),
|
||||
CONSTRAINT vote_value CHECK (vote = 1 OR vote = -1)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.message_content (
|
||||
id SERIAL
|
||||
NOT NULL
|
||||
PRIMARY KEY,
|
||||
message_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.message(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
compose_date TIMESTAMP WITH TIME ZONE
|
||||
NOT NULL
|
||||
DEFAULT CURRENT_TIMESTAMP,
|
||||
content VARCHAR(4096)
|
||||
NOT NULL,
|
||||
approved BOOLEAN
|
||||
NOT NULL
|
||||
DEFAULT FALSE,
|
||||
|
||||
CONSTRAINT content_min_length CHECK (LENGTH(content) >= 2)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.message_initial (
|
||||
message_id INT
|
||||
NOT NULL
|
||||
PRIMARY KEY
|
||||
REFERENCES doki8902.message(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
content_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.message_content(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.post_category (
|
||||
id SERIAL
|
||||
NOT NULL
|
||||
PRIMARY KEY,
|
||||
name VARCHAR(100)
|
||||
NOT NULL,
|
||||
|
||||
CONSTRAINT name_min_length CHECK (LENGTH(name) >= 2)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.post (
|
||||
message_id INT
|
||||
NOT NULL
|
||||
PRIMARY KEY
|
||||
REFERENCES doki8902.message(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
name VARCHAR(255)
|
||||
NOT NULL,
|
||||
category_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.post_category(id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE CASCADE,
|
||||
|
||||
CONSTRAINT name_min_length CHECK (LENGTH(name) >= 2)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS doki8902.comment (
|
||||
message_id INT
|
||||
NOT NULL
|
||||
PRIMARY KEY
|
||||
REFERENCES doki8902.message(id),
|
||||
parent_comment_id INT
|
||||
REFERENCES doki8902.comment(message_id)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE,
|
||||
post_id INT
|
||||
NOT NULL
|
||||
REFERENCES doki8902.post(message_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
---------------------------
|
||||
---== VIEWS ==-------------
|
||||
|
||||
CREATE OR REPLACE VIEW doki8902.message_votes AS (
|
||||
SELECT
|
||||
v.message_id,
|
||||
SUM(CASE WHEN vote > 0 THEN 1 ELSE 0 END) AS "likes",
|
||||
SUM(CASE WHEN vote < 0 THEN 1 ELSE 0 END) AS "dislikes",
|
||||
SUM(vote) AS "score"
|
||||
FROM
|
||||
doki8902.message_vote v
|
||||
GROUP BY v.message_id
|
||||
);
|
||||
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS doki8902.message_content_latest AS (
|
||||
WITH "content_edits" AS (
|
||||
SELECT
|
||||
m.message_id,
|
||||
MAX(m.id) AS "latest_id",
|
||||
COUNT(*) AS "edit_count"
|
||||
FROM
|
||||
doki8902.message_content m
|
||||
WHERE
|
||||
m.approved
|
||||
GROUP BY m.message_id
|
||||
)
|
||||
SELECT
|
||||
e.*,
|
||||
CASE
|
||||
WHEN e.edit_count = 1 THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS "original",
|
||||
i.content AS "latest_content",
|
||||
i.compose_date AS "latest_content_date"
|
||||
FROM
|
||||
doki8902.message_content i
|
||||
JOIN
|
||||
content_edits e
|
||||
ON e.message_id = i.message_id
|
||||
WHERE
|
||||
i.id = e.latest_id
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW doki8902.post_comment_metrics AS (
|
||||
SELECT
|
||||
post_id,
|
||||
COUNT(c.*) AS "comment_count",
|
||||
COUNT(DISTINCT m.author_id) AS "user_count",
|
||||
MAX(m.created_date) AS "latest_activity",
|
||||
SUM(v.likes) AS "likes",
|
||||
SUM(v.dislikes) AS "dislikes",
|
||||
SUM(v.score) AS "score",
|
||||
EXTRACT(DAY FROM AGE(MAX(m.created_date))) AS "age"
|
||||
FROM
|
||||
doki8902.comment c
|
||||
JOIN
|
||||
doki8902.message AS m
|
||||
ON m.id = c.message_id
|
||||
JOIN
|
||||
doki8902.message_votes AS v
|
||||
ON v.message_id = c.message_id
|
||||
GROUP BY c.post_id
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW doki8902.message_post AS (
|
||||
WITH "post_metrics" AS (
|
||||
SELECT
|
||||
m.id,
|
||||
m.author_id,
|
||||
m.created_date,
|
||||
m.soft_delete,
|
||||
p.name,
|
||||
p.category_id,
|
||||
v.likes,
|
||||
v.dislikes,
|
||||
v.score,
|
||||
COALESCE(s.comment_count, 0) AS "comment_count",
|
||||
COALESCE(s.user_count, 0) AS "user_count",
|
||||
COALESCE(s.latest_activity, m.created_date) AS "latest_activity",
|
||||
COALESCE(s.likes, 0) + COALESCE(s.dislikes, 0) + v.likes + v.dislikes + COALESCE(s.comment_count, 0) + COALESCE(s.user_count, 0) AS "engagement",
|
||||
COALESCE(s.age, EXTRACT(DAY FROM AGE(m.created_date))) AS "age"
|
||||
FROM
|
||||
doki8902.post p
|
||||
JOIN
|
||||
doki8902.message AS m
|
||||
ON p.message_id = m.id
|
||||
JOIN
|
||||
doki8902.message_votes AS v
|
||||
ON v.message_id = m.id
|
||||
LEFT OUTER JOIN
|
||||
doki8902.post_comment_metrics AS s
|
||||
ON s.post_id = p.message_id
|
||||
)
|
||||
SELECT
|
||||
p.*,
|
||||
(p.engagement + p.user_count + p.comment_count) / (p.age + 1) AS "relevancy",
|
||||
l.edit_count,
|
||||
l.original,
|
||||
CASE
|
||||
WHEN l.original IS NOT NULL THEN True
|
||||
ELSE False
|
||||
END AS "reviewed",
|
||||
COALESCE(l.latest_content, (
|
||||
SELECT c1.content
|
||||
FROM doki8902.message_initial i1
|
||||
JOIN doki8902.message_content c1 ON c1.id = i1.content_id
|
||||
WHERE i1.message_id = p.id
|
||||
)) AS "latest_content",
|
||||
l.latest_content_date
|
||||
FROM
|
||||
post_metrics p
|
||||
LEFT OUTER JOIN
|
||||
doki8902.message_content_latest AS l
|
||||
ON l.message_id = p.id
|
||||
LEFT OUTER JOIN
|
||||
doki8902.post_comment_metrics AS s
|
||||
ON s.post_id = p.id
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW doki8902.message_comment AS (
|
||||
SELECT
|
||||
m.*,
|
||||
c.post_id,
|
||||
c.parent_comment_id,
|
||||
l.edit_count,
|
||||
l.original,
|
||||
l.latest_content_date,
|
||||
v.likes,
|
||||
v.dislikes,
|
||||
v.score,
|
||||
CASE
|
||||
WHEN l.original IS NOT NULL THEN True
|
||||
ELSE False
|
||||
END AS "reviewed",
|
||||
COALESCE(l.latest_content, (
|
||||
SELECT c1.content
|
||||
FROM doki8902.message_initial i1
|
||||
JOIN doki8902.message_content c1 ON c1.id = i1.content_id
|
||||
WHERE i1.message_id = m.id
|
||||
)) AS "latest_content"
|
||||
FROM
|
||||
doki8902.message m
|
||||
JOIN
|
||||
doki8902.comment c
|
||||
ON c.message_id = m.id
|
||||
LEFT OUTER JOIN
|
||||
doki8902.message_content_latest l
|
||||
ON l.message_id = m.id
|
||||
JOIN
|
||||
doki8902.message_votes v
|
||||
ON v.message_id = m.id
|
||||
);
|
||||
|
||||
---------------------------
|
||||
---== INDICES ==-----------
|
||||
|
||||
CREATE UNIQUE INDEX idx_user_username
|
||||
ON doki8902.user(username);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user
|
||||
ON doki8902.user_session
|
||||
USING HASH (user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_message_author
|
||||
ON doki8902.message
|
||||
USING HASH (author_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_message_content_message
|
||||
ON doki8902.message_content(message_id, compose_date); /* TODO */
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_message_vote_message
|
||||
ON doki8902.message_vote
|
||||
USING HASH (message_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_post_category
|
||||
ON doki8902.post
|
||||
USING HASH (category_id);
|
||||
|
||||
CREATE UNIQUE INDEX idx_post_category_name
|
||||
ON doki8902.post_category(name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_comment_post
|
||||
ON doki8902.comment
|
||||
USING HASH (post_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_comment_parent
|
||||
ON doki8902.comment
|
||||
USING HASH (parent_comment_id);
|
||||
|
||||
CREATE UNIQUE INDEX idx_message_id
|
||||
ON doki8902.message_content_latest(message_id);
|
||||
|
||||
---------------------------
|
||||
---== FUNCTIONS ==---------
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_log_action(
|
||||
user_id INT,
|
||||
type doki8902.user_action_type,
|
||||
details TEXT
|
||||
) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
INSERT INTO doki8902.user_action_log (user_id, type, details) VALUES (
|
||||
user_id, type, details
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_log_message_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM doki8902.func_log_action(
|
||||
(SELECT author_id FROM doki8902.message WHERE id = NEW.message_id),
|
||||
CAST(TG_ARGV[0] AS doki8902.user_action_type),
|
||||
CAST(NEW.message_id AS TEXT)
|
||||
);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_log_user_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM doki8902.func_log_action(
|
||||
NEW.id,
|
||||
CAST(TG_ARGV[0] AS doki8902.user_action_type),
|
||||
TG_ARGV[1]
|
||||
);
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_log_user_restrict(
|
||||
user_id INT,
|
||||
reason TEXT
|
||||
) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
PERFORM doki8902.func_log_action(user_id, 'user_restrict', reason);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_update_message_contents_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
REFRESH MATERIALIZED VIEW CONCURRENTLY doki8902.message_content_latest;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_approve_all_pending_content() RETURNS VOID AS $$
|
||||
BEGIN
|
||||
UPDATE doki8902.message_content
|
||||
SET approved = TRUE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
---------------------------
|
||||
---== TRIGGERS ==----------
|
||||
|
||||
--- USERS
|
||||
CREATE TRIGGER message_user_join_log
|
||||
AFTER INSERT
|
||||
ON doki8902.user
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_log_user_trigger('user_join', '');
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_remove_sessions_password_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
DELETE FROM doki8902.user_session WHERE user_id = NEW.id;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER user_password_change
|
||||
AFTER UPDATE OF password
|
||||
ON doki8902.user
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_remove_sessions_password_trigger();
|
||||
|
||||
--- MESSAGE CONTENT
|
||||
CREATE TRIGGER message_content_update_refresh
|
||||
AFTER UPDATE OF approved
|
||||
ON doki8902.message_content
|
||||
FOR EACH STATEMENT
|
||||
EXECUTE FUNCTION doki8902.func_update_message_contents_trigger();
|
||||
|
||||
--- MESSAGES
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_vote_own_created_message_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO doki8902.message_vote (message_id, user_id, vote) VALUES (
|
||||
NEW.id, NEW.author_id, 1
|
||||
);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_create_vote_auto
|
||||
AFTER INSERT
|
||||
ON doki8902.message
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_vote_own_created_message_trigger();
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_log_message_delete_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF OLD.soft_delete = FALSE AND NEW.soft_delete = TRUE THEN
|
||||
IF EXISTS(SELECT * FROM doki8902.post WHERE NEW.id = message_id) THEN
|
||||
PERFORM doki8902.func_log_action(
|
||||
(SELECT author_id FROM doki8902.message WHERE id = NEW.message_id),
|
||||
'post_delete',
|
||||
CAST(NEW.message_id AS TEXT)
|
||||
);
|
||||
ELSIF EXISTS(SELECT * FROM doki8902.comment WHERE NEW.id = message_id) THEN
|
||||
PERFORM doki8902.func_log_action(
|
||||
(SELECT author_id FROM doki8902.message WHERE id = NEW.message_id),
|
||||
'comment_delete',
|
||||
CAST(NEW.message_id AS TEXT)
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_delete_log
|
||||
AFTER UPDATE
|
||||
ON doki8902.message
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_log_message_delete_trigger();
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_require_user_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NOT(EXISTS(SELECT * FROM doki8902.user WHERE id = NEW.author_id))
|
||||
THEN
|
||||
RAISE EXCEPTION 'Newly created Message must have a valid user %', NEW.author_id USING CONSTRAINT = 'message_require_user';
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_require_user
|
||||
BEFORE INSERT
|
||||
ON doki8902.message
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_require_user_trigger();
|
||||
|
||||
--- POSTS
|
||||
CREATE TRIGGER message_post_create_log
|
||||
AFTER INSERT
|
||||
ON doki8902.post
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_log_message_trigger('post_create');
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_create_message_post_trigger() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
message_id INTEGER;
|
||||
content_id INTEGER;
|
||||
BEGIN
|
||||
INSERT INTO doki8902.message(author_id)
|
||||
VALUES (NEW.author_id)
|
||||
RETURNING id INTO message_id;
|
||||
|
||||
INSERT INTO doki8902.post(message_id, name, category_id)
|
||||
VALUES (message_id, NEW.name, NEW.category_id);
|
||||
|
||||
IF (NEW.reviewed = TRUE) THEN
|
||||
INSERT INTO doki8902.message_content(message_id, content, approved)
|
||||
VALUES (message_id, NEW.latest_content, TRUE)
|
||||
RETURNING id INTO content_id;
|
||||
ELSE
|
||||
INSERT INTO doki8902.message_content(message_id, content)
|
||||
VALUES (message_id, NEW.latest_content)
|
||||
RETURNING id INTO content_id;
|
||||
END IF;
|
||||
|
||||
INSERT INTO doki8902.message_initial(message_id, content_id)
|
||||
VALUES (message_id, content_id);
|
||||
|
||||
NEW.id := message_id;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_post_insert
|
||||
INSTEAD OF INSERT
|
||||
ON doki8902.message_post
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_create_message_post_trigger();
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_update_message_post_content_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF (OLD.latest_content IS DISTINCT FROM NEW.latest_content) THEN
|
||||
INSERT INTO doki8902.message_content(message_id, content) VALUES (NEW.id, NEW.latest_content);
|
||||
END IF;
|
||||
|
||||
IF (OLD.category_id IS DISTINCT FROM NEW.category_id) THEN
|
||||
UPDATE doki8902.post
|
||||
SET category_id = NEW.category_id
|
||||
WHERE message_id = NEW.id;
|
||||
END IF;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_post_update_content
|
||||
INSTEAD OF UPDATE
|
||||
ON doki8902.message_post
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_update_message_post_content_trigger();
|
||||
|
||||
--- COMMENTS
|
||||
CREATE TRIGGER message_post_comment_log
|
||||
AFTER INSERT
|
||||
ON doki8902.comment
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_log_message_trigger('comment_create');
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_create_message_comment_trigger() RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
message_id INTEGER;
|
||||
content_id INTEGER;
|
||||
BEGIN
|
||||
IF (NEW.parent_comment_id <> NULL AND NOT(
|
||||
EXISTS(
|
||||
SELECT
|
||||
c.*
|
||||
FROM
|
||||
doki8902.comment c
|
||||
WHERE
|
||||
c.post_id = NEW.post_id
|
||||
AND c.message_id = NEW.parent_comment_id
|
||||
)
|
||||
)) THEN
|
||||
RAISE EXCEPTION 'Parent Comment ID % does NOT exist for the Post %', NEW.parent_comment_id, NEW.post_id;
|
||||
END IF;
|
||||
|
||||
INSERT INTO doki8902.message(author_id)
|
||||
VALUES (NEW.author_id)
|
||||
RETURNING id INTO message_id;
|
||||
|
||||
INSERT INTO doki8902.comment(message_id, post_id, parent_comment_id)
|
||||
VALUES (message_id, NEW.post_id, NEW.parent_comment_id);
|
||||
|
||||
IF (NEW.reviewed = TRUE) THEN
|
||||
INSERT INTO doki8902.message_content(message_id, content, approved)
|
||||
VALUES (message_id, NEW.latest_content, TRUE)
|
||||
RETURNING id INTO content_id;
|
||||
ELSE
|
||||
INSERT INTO doki8902.message_content(message_id, content)
|
||||
VALUES (message_id, NEW.latest_content)
|
||||
RETURNING id INTO content_id;
|
||||
END IF;
|
||||
|
||||
INSERT INTO doki8902.message_initial(message_id, content_id)
|
||||
VALUES (message_id, content_id);
|
||||
|
||||
NEW.id := message_id;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_comment_insert
|
||||
INSTEAD OF INSERT
|
||||
ON doki8902.message_comment
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_create_message_comment_trigger();
|
||||
|
||||
---
|
||||
CREATE OR REPLACE FUNCTION doki8902.func_update_message_comment_content_trigger() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF (OLD.latest_content IS DISTINCT FROM NEW.latest_content)
|
||||
THEN
|
||||
INSERT INTO doki8902.message_content(message_id, content) VALUES (NEW.id, NEW.latest_content);
|
||||
END IF;
|
||||
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER message_comment_update_content
|
||||
INSTEAD OF UPDATE
|
||||
ON doki8902.message_comment
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION doki8902.func_update_message_comment_content_trigger();
|
||||
31
db/drop.sql
Normal file
31
db/drop.sql
Normal file
@ -0,0 +1,31 @@
|
||||
DROP TABLE IF EXISTS doki8902.comment CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.post CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.post_category CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.message_initial CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.message_content CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.message_vote CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.message CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.user_action_log CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.user_session CASCADE;
|
||||
DROP TABLE IF EXISTS doki8902.user CASCADE;
|
||||
|
||||
|
||||
DROP FUNCTION IF EXISTS doki8902.func_update_message_comment_content_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_create_message_comment_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_update_message_post_content_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_create_message_post_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_require_user_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_log_message_delete_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_vote_own_created_message_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_approve_all_pending_content;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_remove_sessions_password_trigger;
|
||||
|
||||
DROP FUNCTION IF EXISTS doki8902.func_update_message_contents_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_log_user_restrict;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_log_user_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_log_message_trigger;
|
||||
DROP FUNCTION IF EXISTS doki8902.func_log_action;
|
||||
|
||||
|
||||
DROP TYPE IF EXISTS doki8902.user_access_type;
|
||||
DROP TYPE IF EXISTS doki8902.user_action_type;
|
||||
124
db/insert.sql
Normal file
124
db/insert.sql
Normal file
@ -0,0 +1,124 @@
|
||||
-- CREATE SETUP
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO doki8902.post_category(name) VALUES
|
||||
('Technology'),
|
||||
('Food'),
|
||||
('Games'),
|
||||
('Reading'),
|
||||
('Movies'),
|
||||
('Sports'),
|
||||
('Travel');
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- CREATE USERS
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO doki8902.user(username, password) VALUES
|
||||
('Alice', 'pass123'),
|
||||
('Bob', 'qwerty'),
|
||||
('Charlie', 'abc456'),
|
||||
('David', 'secret'),
|
||||
('Eve', 'p@ssw0rd'),
|
||||
('Frank', 'admin123'),
|
||||
('Grace', 'letmein'),
|
||||
('Henry', 'changeme'),
|
||||
('Isabel', 'ilovecats'),
|
||||
('Jack', 'welcome'),
|
||||
('BotAccount', 'qwertyasdf');
|
||||
|
||||
INSERT INTO doki8902.user(username, password, access) VALUES ('admin', '$argon2id$v=19$m=65536,t=4,p=1$QNRzi0vaVyjR/AszHyJF9Q$+2rRyTHHMIYjYCRSZEpjRGWLjWWl1Jval8JH/l2ArymxEbSjctGWMT4X0neqVeN9yC32LO9utmH9WHr0gJXpQQ', 'elevated');
|
||||
|
||||
INSERT INTO doki8902.user_session(user_id) VALUES (1), (2), (3), (5), (5), (6), (7), (8), (9), (11);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- NORMAL USE
|
||||
|
||||
BEGIN;
|
||||
|
||||
SELECT doki8902.func_log_user_restrict(4, 'Excessive spamming');
|
||||
SELECT doki8902.func_log_user_restrict(5, 'Inappropriate content');
|
||||
|
||||
INSERT INTO doki8902.message_post(author_id, category_id, name, latest_content) VALUES
|
||||
(4, 1, 'Best practices for data security', 'It''s crucial to keep your software updated to avoid security breaches.'),
|
||||
(1, 2, 'Grilling tips for beginners', 'Always preheat your grill for at least 15 minutes.'),
|
||||
(7, 3, 'Upcoming RPG games 2024', 'Anyone excited about the new Dragon Quest release?'),
|
||||
(2, 4, 'Book recommendations for historical fiction', 'I just finished "The Nightingale" and loved it!'),
|
||||
(10, 5, 'Which director makes the best thrillers?', 'I think Christopher Nolan does a great job with complex narratives.'),
|
||||
(3, 6, 'Is tennis the most physically demanding sport?', 'The agility and stamina required are intense.'),
|
||||
(5, 7, 'Budget-friendly European destinations', 'Portugal is quite affordable and beautiful.'),
|
||||
(6, 1, 'The future of quantum computing', 'Quantum computers could revolutionize how we process data.'),
|
||||
(8, 2, 'What''s your favorite type of cheese?', 'I can never get enough of Gouda!'),
|
||||
(9, 5, 'Horror movies that are actually scary', 'The Conjuring series still gives me chills.');
|
||||
|
||||
INSERT INTO doki8902.message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(9, 1, 'Absolutely! Regular updates are a must.', NULL),
|
||||
(8, 2, 'Thanks for the tip! Any specific grills you recommend?', NULL),
|
||||
(2, 2, 'Charcoal grills give the best flavor in my opinion.', NULL),
|
||||
(7, 3, 'I heard it''s coming out next fall.', NULL),
|
||||
(3, 3, 'Can''t wait for it, it sounds amazing!', NULL),
|
||||
(8, 4, 'I love historical fiction too, any other suggestions?', NULL),
|
||||
(1, 5, 'Agreed, Nolan is a mastermind.', NULL),
|
||||
(3, 5, 'What about Alfred Hitchcock?', NULL),
|
||||
(5, 6, 'I think boxing might compete for that title.', NULL),
|
||||
(7, 7, 'Don''t forget about Greece, especially during off-peak seasons!', NULL),
|
||||
(1, 8, 'How soon do you think we''ll see practical applications?', NULL),
|
||||
(7, 9, 'Cheddar is great for cooking but nothing beats Brie for snacking.', NULL),
|
||||
(9, 9, 'Gouda is indeed delicious!', NULL),
|
||||
(4, 10, 'Those films are great, have you seen "Insidious" as well?', NULL);
|
||||
|
||||
INSERT INTO doki8902.message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(4, 1, 'That''s a good point. Do you recommend any specific antivirus software?', 11),
|
||||
(9, 2, 'I''ve been looking at Weber grills, heard they''re good.', 12),
|
||||
(3, 2, 'Yes, but I think gas grills are easier for beginners.', 13),
|
||||
(7, 3, 'I hope they add more customization options.', 15),
|
||||
(2, 4, 'You should check out "Pillars of the Earth" by Ken Follett.', 16),
|
||||
(6, 5, 'True, but don''t forget about M. Night Shyamalan.', 18),
|
||||
(2, 6, 'Agree on boxing, but MMA seems tougher.', 19),
|
||||
(6, 7, 'Also Bulgaria in the autumn is surprisingly beautiful.', 20),
|
||||
(8, 9, 'Totally agree on Brie, especially with a good wine.', 22),
|
||||
(9, 10, 'Insidious was good but it doesn''t top The Conjuring for me.', 24);
|
||||
|
||||
INSERT INTO doki8902.message_vote(message_id, user_id, vote) VALUES
|
||||
(1, 5, 1),
|
||||
(2, 4, 1),
|
||||
(3, 6, -1),
|
||||
(4, 3, 1),
|
||||
(5, 8, 1),
|
||||
(8, 2, 1),
|
||||
(11, 1, 1),
|
||||
(12, 10, -1),
|
||||
(14, 6, 1),
|
||||
(15, 4, 1),
|
||||
(17, 4, 1),
|
||||
(19, 2, 1),
|
||||
(20, 1, 1),
|
||||
(22, 3, 1),
|
||||
(23, 5, 1),
|
||||
(25, 7, 1),
|
||||
(28, 8, 1),
|
||||
(31, 6, -1),
|
||||
(32, 9, 1),
|
||||
(34, 10, 1),
|
||||
-- Adding 5 dislikes to message 10
|
||||
(10, 2, -1),
|
||||
(10, 6, -1),
|
||||
(10, 7, -1),
|
||||
(10, 8, -1),
|
||||
(10, 10, -1),
|
||||
-- Adding 5 dislikes to message 11
|
||||
(11, 2, -1),
|
||||
(11, 6, -1),
|
||||
(11, 7, -1),
|
||||
(11, 8, -1),
|
||||
(11, 3, -1);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ACCEPT ALL CURRENT CONTENT
|
||||
|
||||
SELECT doki8902.func_approve_all_pending_content();
|
||||
41
db/insert_old.sql
Normal file
41
db/insert_old.sql
Normal file
@ -0,0 +1,41 @@
|
||||
-- CREATE SETUP
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO doki8902.post_category(name) VALUES
|
||||
('Technology'),
|
||||
('Food'),
|
||||
('Games'),
|
||||
('Reading'),
|
||||
('Movies'),
|
||||
('Sports');
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- CREATE USERS
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO doki8902.user(username, password) VALUES
|
||||
('Alice', 'pass123'),
|
||||
('Bob', 'qwerty'),
|
||||
('Charlie', 'abc456'),
|
||||
('BotAccount', 'qwertyasdf');
|
||||
|
||||
INSERT INTO doki8902.user_permission(user_id, permission, state) VALUES
|
||||
(1, 'delete', TRUE),
|
||||
(1, 'edit', TRUE),
|
||||
(2, 'admin', TRUE),
|
||||
(4, 'post', FALSE);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- NORMAL USE
|
||||
|
||||
SELECT doki8902.func_log_user_restrict(2, 'Spam and botting');
|
||||
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES
|
||||
(3, 2, 'What''s your favorite dessert?', 'Personally, I like Ice Cream');
|
||||
|
||||
INSERT INTO message_comment (author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(2, 1, 'Chocolate Mousse', NULL);
|
||||
24
db/insertp2.sql
Normal file
24
db/insertp2.sql
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
-- EDITS
|
||||
|
||||
UPDATE doki8902.message_comment SET latest_content = 'I heard it''s coming out next fall. Does anyone know if there will be VR support?' WHERE id = 14;
|
||||
UPDATE doki8902.message_comment SET latest_content = 'I love historical fiction too. Could anyone recommend other novels similar to "The Nightingale"?' WHERE id = 16;
|
||||
UPDATE doki8902.message_comment SET latest_content = 'Agreed, Christopher Nolan''s storytelling is brilliant, especially in movies like ''Inception''.' WHERE id = 17;
|
||||
UPDATE doki8902.message_comment SET latest_content = 'Actually, I''ve come to think that marathon running might top them all in terms of endurance and physical demand.' WHERE id = 19;
|
||||
UPDATE doki8902.message_comment SET latest_content = 'I''ve started to really enjoy Manchego lately, especially with a slice of quince paste. It pairs well with both cooking and snacking!' WHERE id = 22;
|
||||
|
||||
-- ACCEPT ALL NEW CONTENT
|
||||
|
||||
SELECT doki8902.func_approve_all_pending_content();
|
||||
|
||||
-- DELETE A USER
|
||||
|
||||
DELETE FROM doki8902.user WHERE id = 4;
|
||||
|
||||
-- DELETE MESSAGES SOFT
|
||||
|
||||
UPDATE doki8902.message_comment SET soft_delete = TRUE WHERE id IN (18, 19, 20);
|
||||
|
||||
-- DELETE POST HARD
|
||||
|
||||
DELETE FROM doki8902.post WHERE message_id = 2;
|
||||
1
db/schema.sql
Normal file
1
db/schema.sql
Normal file
@ -0,0 +1 @@
|
||||
CREATE SCHEMA IF NOT EXISTS doki8092;
|
||||
63
db/tests.sql
Normal file
63
db/tests.sql
Normal file
@ -0,0 +1,63 @@
|
||||
-- CANT HAVE SAME USERNAME
|
||||
|
||||
INSERT INTO doki8902.user(username, password) VALUES ('Frank', 'admin123');
|
||||
|
||||
-- CANT HAVE INVALID USERNAME
|
||||
|
||||
INSERT INTO doki8902.user(username, password) VALUES ('Frank@', 'admin123');
|
||||
INSERT INTO doki8902.user(username, password) VALUES ('1', 'admin123');
|
||||
INSERT INTO doki8902.user(username, password) VALUES ('ABCDEABCDE1234512345ABCDEABCDE1234512345', 'admin123');
|
||||
|
||||
-- CANT HAVE REPEATING CATEGORIES
|
||||
|
||||
INSERT INTO doki8902.post_category(name) VALUES ('Technology');
|
||||
|
||||
-- CANT POST WITH INVALID AUTHOR
|
||||
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES
|
||||
(100, 1, 'Best practices for data security', 'It''s crucial to keep your software updated to avoid security breaches.');
|
||||
|
||||
-- CANT POST FOR NON EXISTENT CATEGORY
|
||||
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES
|
||||
(2, 100, 'Best practices for data security', 'It''s crucial to keep your software updated to avoid security breaches.');
|
||||
|
||||
-- CANT COMMENT WITH INVALID AUTHOR
|
||||
|
||||
INSERT INTO message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(100, 1, 'Absolutely! Regular updates are a must.', NULL);
|
||||
|
||||
-- CANT COMMENT FOR INVALID POST
|
||||
|
||||
INSERT INTO message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(1, 100, 'Absolutely! Regular updates are a must.', NULL);
|
||||
|
||||
-- CANT VOTE TWICE
|
||||
|
||||
INSERT INTO doki8902.message_vote(message_id, user_id, vote) VALUES
|
||||
(1, 4, 1);
|
||||
|
||||
-- CANT VOTE NOT SUPPORTED VALUES
|
||||
|
||||
INSERT INTO doki8902.message_vote(message_id, user_id, vote) VALUES
|
||||
(1, 5, 5);
|
||||
|
||||
-- CANT VOTE WITH INVALID IDS
|
||||
|
||||
INSERT INTO doki8902.message_vote(message_id, user_id, vote) VALUES (100, 5, 1);
|
||||
INSERT INTO doki8902.message_vote(message_id, user_id, vote) VALUES (1, 100, 1);
|
||||
|
||||
-- CANT REPLY TO COMMENT THAT DOES NOT EXISTS IN POST
|
||||
|
||||
INSERT INTO doki8902.message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES
|
||||
(11, 3, 'test', 20);
|
||||
|
||||
-- CANT POST EMPTY MESSAGES
|
||||
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES (2, 1, '', 'Content');
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES (2, 1, 'Name', '');
|
||||
INSERT INTO doki8902.message_comment(author_id, post_id, latest_content, parent_comment_id) VALUES (11, 1, '', NULL);
|
||||
|
||||
-- CANT POST WITHOUT AN EXISTING USER
|
||||
|
||||
INSERT INTO message_post(author_id, category_id, name, latest_content) VALUES (NULL, 1, 'Name', 'Content');
|
||||
Loading…
x
Reference in New Issue
Block a user