From c45b7dd5b0f09154ba9811c84e5ff7ab1fe2670b Mon Sep 17 00:00:00 2001 From: Donatas Kirda Date: Sun, 11 May 2025 21:55:40 +0300 Subject: [PATCH] Add: DB SQL Setup queries --- db/clear.sql | 16 ++ db/create.sql | 675 ++++++++++++++++++++++++++++++++++++++++++++++ db/drop.sql | 31 +++ db/insert.sql | 124 +++++++++ db/insert_old.sql | 41 +++ db/insertp2.sql | 24 ++ db/schema.sql | 1 + db/tests.sql | 63 +++++ 8 files changed, 975 insertions(+) create mode 100644 db/clear.sql create mode 100644 db/create.sql create mode 100644 db/drop.sql create mode 100644 db/insert.sql create mode 100644 db/insert_old.sql create mode 100644 db/insertp2.sql create mode 100644 db/schema.sql create mode 100644 db/tests.sql diff --git a/db/clear.sql b/db/clear.sql new file mode 100644 index 0000000..12d8564 --- /dev/null +++ b/db/clear.sql @@ -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; \ No newline at end of file diff --git a/db/create.sql b/db/create.sql new file mode 100644 index 0000000..63392ba --- /dev/null +++ b/db/create.sql @@ -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(); diff --git a/db/drop.sql b/db/drop.sql new file mode 100644 index 0000000..5ceca09 --- /dev/null +++ b/db/drop.sql @@ -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; diff --git a/db/insert.sql b/db/insert.sql new file mode 100644 index 0000000..428f737 --- /dev/null +++ b/db/insert.sql @@ -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(); diff --git a/db/insert_old.sql b/db/insert_old.sql new file mode 100644 index 0000000..37481ba --- /dev/null +++ b/db/insert_old.sql @@ -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); diff --git a/db/insertp2.sql b/db/insertp2.sql new file mode 100644 index 0000000..cd0fe24 --- /dev/null +++ b/db/insertp2.sql @@ -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; diff --git a/db/schema.sql b/db/schema.sql new file mode 100644 index 0000000..fff66ad --- /dev/null +++ b/db/schema.sql @@ -0,0 +1 @@ +CREATE SCHEMA IF NOT EXISTS doki8092; diff --git a/db/tests.sql b/db/tests.sql new file mode 100644 index 0000000..1b6af50 --- /dev/null +++ b/db/tests.sql @@ -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');