From 68884daf1158f4347c431ce9375c24e6e1bfa8d8 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sat, 16 May 2026 10:07:12 +0200 Subject: [PATCH] feat(12-01): add discussion schema and queries --- backend/internal/db/queries/discussion.sql | 47 ++++++++++++++++++++++ backend/migrations/0009_discussion.sql | 42 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 backend/internal/db/queries/discussion.sql create mode 100644 backend/migrations/0009_discussion.sql diff --git a/backend/internal/db/queries/discussion.sql b/backend/internal/db/queries/discussion.sql new file mode 100644 index 0000000..f58a3ad --- /dev/null +++ b/backend/internal/db/queries/discussion.sql @@ -0,0 +1,47 @@ +-- name: ListDiscussionMessagesByTablo :many +SELECT discussion_messages.id, + discussion_messages.tablo_id, + discussion_messages.author_user_id, + users.email AS author_email, + discussion_messages.body, + discussion_messages.created_at, + discussion_messages.updated_at, + discussion_messages.edited_at, + discussion_messages.edited_by_user_id, + discussion_messages.deleted_at, + discussion_messages.deleted_by_user_id +FROM discussion_messages +JOIN users ON users.id = discussion_messages.author_user_id +WHERE discussion_messages.tablo_id = $1 +ORDER BY discussion_messages.created_at, discussion_messages.id; + +-- name: CreateDiscussionMessage :one +INSERT INTO discussion_messages (tablo_id, author_user_id, body) +VALUES ($1, $2, $3) +RETURNING id, tablo_id, author_user_id, body, created_at, updated_at, edited_at, edited_by_user_id, deleted_at, deleted_by_user_id; + +-- name: GetDiscussionMessageWithAuthor :one +SELECT discussion_messages.id, + discussion_messages.tablo_id, + discussion_messages.author_user_id, + users.email AS author_email, + discussion_messages.body, + discussion_messages.created_at, + discussion_messages.updated_at, + discussion_messages.edited_at, + discussion_messages.edited_by_user_id, + discussion_messages.deleted_at, + discussion_messages.deleted_by_user_id +FROM discussion_messages +JOIN users ON users.id = discussion_messages.author_user_id +WHERE discussion_messages.id = $1 + AND discussion_messages.tablo_id = $2; + +-- name: UpsertDiscussionReadState :one +INSERT INTO discussion_read_states (tablo_id, user_id, last_read_message_id, last_read_at) +VALUES ($1, $2, $3, now()) +ON CONFLICT (tablo_id, user_id) +DO UPDATE SET last_read_message_id = EXCLUDED.last_read_message_id, + last_read_at = now(), + updated_at = now() +RETURNING tablo_id, user_id, last_read_message_id, last_read_at, created_at, updated_at; diff --git a/backend/migrations/0009_discussion.sql b/backend/migrations/0009_discussion.sql new file mode 100644 index 0000000..a3cc647 --- /dev/null +++ b/backend/migrations/0009_discussion.sql @@ -0,0 +1,42 @@ +-- migrations/0009_discussion.sql +-- Phase 12: Native Tablo Chat + +-- +goose Up + +CREATE TABLE discussion_messages ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + tablo_id uuid NOT NULL REFERENCES tablos(id) ON DELETE CASCADE, + author_user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, + body text NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + edited_at timestamptz, + edited_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL, + deleted_at timestamptz, + deleted_by_user_id uuid REFERENCES users(id) ON DELETE SET NULL, + CONSTRAINT discussion_messages_body_not_blank CHECK (length(trim(body)) > 0), + CONSTRAINT discussion_messages_body_max_length CHECK (char_length(body) <= 10000) +); + +CREATE INDEX discussion_messages_tablo_created_idx ON discussion_messages(tablo_id, created_at, id); +CREATE INDEX discussion_messages_author_idx ON discussion_messages(author_user_id); + +CREATE TABLE discussion_read_states ( + tablo_id uuid NOT NULL REFERENCES tablos(id) ON DELETE CASCADE, + user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + last_read_message_id uuid REFERENCES discussion_messages(id) ON DELETE SET NULL, + last_read_at timestamptz NOT NULL DEFAULT now(), + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (tablo_id, user_id) +); + +CREATE INDEX discussion_read_states_user_idx ON discussion_read_states(user_id, tablo_id); + +-- +goose Down + +DROP INDEX IF EXISTS discussion_read_states_user_idx; +DROP TABLE IF EXISTS discussion_read_states; +DROP INDEX IF EXISTS discussion_messages_author_idx; +DROP INDEX IF EXISTS discussion_messages_tablo_created_idx; +DROP TABLE IF EXISTS discussion_messages;