Add unified post storage.
CI / test (pull_request) Successful in 6m17s

Provide one creation path for roots and replies, recursive thread loading, body-only updates, root listings, and post voting across PostgreSQL and the in-memory test store.
This commit is contained in:
2026-08-27 00:10:47 -07:00
parent c5ef15ae1f
commit fc8f286c34
8 changed files with 1308 additions and 2 deletions
+112
View File
@@ -0,0 +1,112 @@
-- name: CreatePost :exec
INSERT INTO posts (
id, parent_id, author_id, title, body, city, post_date, hidden, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
-- name: GetPost :one
SELECT
p.id, p.parent_id, p.author_id, u.name AS author_name, u.role AS author_role,
p.title, p.body, p.city, p.post_date, p.hidden, p.created_at, p.updated_at
FROM posts p
JOIN users u ON u.id = p.author_id
WHERE p.id = $1;
-- name: ListPostThread :many
WITH RECURSIVE thread AS (
SELECT p.*
FROM posts p
WHERE p.id = $1 AND p.parent_id IS NULL
UNION ALL
SELECT child.*
FROM posts child
JOIN thread parent ON child.parent_id = parent.id
)
SELECT
thread.id, thread.parent_id, thread.author_id,
u.name AS author_name, u.role AS author_role,
thread.title, thread.body, thread.city, thread.post_date,
thread.hidden, thread.created_at, thread.updated_at
FROM thread
JOIN users u ON u.id = thread.author_id
ORDER BY thread.created_at, thread.id;
-- name: UpdatePost :execrows
UPDATE posts
SET body = $2, updated_at = $3
WHERE id = $1;
-- name: ListRootPosts :many
WITH RECURSIVE roots AS (
SELECT p.*
FROM posts p
WHERE p.parent_id IS NULL
AND p.post_date = sqlc.arg(post_date)
AND p.hidden = 0
),
thread AS (
SELECT roots.id AS root_id, roots.id AS post_id, roots.author_id
FROM roots
UNION ALL
SELECT thread.root_id, child.id, child.author_id
FROM thread
JOIN posts child ON child.parent_id = thread.post_id
),
answered AS (
SELECT thread.root_id, bool_or(u.role = 'admin' AND thread.post_id <> thread.root_id) AS answered
FROM thread
JOIN users u ON u.id = thread.author_id
GROUP BY thread.root_id
),
scores AS (
SELECT post_id, COALESCE(SUM(value), 0)::bigint AS score
FROM post_votes
GROUP BY post_id
)
SELECT
roots.id, roots.parent_id, roots.author_id,
u.name AS author_name, u.role AS author_role,
roots.title, roots.body, roots.city, roots.post_date,
roots.hidden, roots.created_at, roots.updated_at,
COALESCE(scores.score, 0)::bigint AS score,
COALESCE(answered.answered, false)::bool AS answered,
COALESCE((
SELECT post_votes.value
FROM post_votes
WHERE post_votes.user_id = sqlc.arg(viewer_id)
AND post_votes.post_id = roots.id
), 0)::bigint AS user_vote
FROM roots
JOIN users u ON u.id = roots.author_id
LEFT JOIN scores ON scores.post_id = roots.id
LEFT JOIN answered ON answered.root_id = roots.id
ORDER BY score DESC, roots.created_at, roots.id
LIMIT sqlc.arg(row_limit);
-- name: PostIsVisibleRoot :one
SELECT EXISTS(
SELECT 1
FROM posts
WHERE id = $1 AND parent_id IS NULL AND hidden = 0
)::bool;
-- name: DeletePostVote :exec
DELETE FROM post_votes
WHERE user_id = $1 AND post_id = $2;
-- name: UpsertPostVoteOnVisibleRoot :execrows
INSERT INTO post_votes (user_id, post_id, value)
SELECT $1, $2, $3
FROM posts p
WHERE p.id = $2 AND p.parent_id IS NULL AND p.hidden = 0
ON CONFLICT (user_id, post_id) DO UPDATE
SET value = excluded.value
WHERE EXISTS (
SELECT 1
FROM posts p2
WHERE p2.id = excluded.post_id AND p2.parent_id IS NULL AND p2.hidden = 0
);