Files
Completed/db/query.sql
T
SansanandClaude Sonnet 5 7fbd403d30 Make the platform list shared across users; keep games private
Platforms no longer have a user_id: there's one global list, unique by
name, that every authenticated user reads from and writes to. Games are
unchanged - still private per user, still referencing platform_id.

- Migration: dropped platform.user_id and its FK, UNIQUE(user_id, name) ->
  UNIQUE(name), index on change_seq alone instead of (user_id, change_seq).
  Amended the one init migration in place rather than stacking a second one,
  since nothing is deployed yet.
- db/query.sql: platform queries no longer take a user_id parameter.
  DeletePlatform's NOT EXISTS guard already checked games from any user (it
  never filtered the subquery by user_id), so deleting a shared platform is
  correctly blocked by any user's games on it, not just the deleter's.
- routes.go: platform handlers still require auth (the list isn't public to
  anonymous requests) but no longer scope by user.
- Tests: updated the mocks for the new signatures, added
  TestListPlatformsIsSharedAcrossUsers.

Verified against a real Postgres: user A creates "PC", user B sees it
immediately, both can add games against it, each only sees their own
games, deleting the platform is blocked while either user has a game on
it, and the name stays globally unique.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 11:34:47 +02:00

125 lines
3.4 KiB
SQL

-- name: CreateUser :one
INSERT INTO users (
email, password_hash, name, bio
) VALUES (
$1, $2, $3, $4
)
RETURNING id, email, name, bio, created_at;
-- name: GetUserByEmail :one
SELECT id, email, password_hash, name, bio, created_at
FROM users
WHERE email = $1;
-- name: GetUserByID :one
SELECT id, email, name, bio, created_at
FROM users
WHERE id = $1;
-- name: CreateSession :exec
INSERT INTO sessions (
token_hash, user_id, expires_at
) VALUES (
$1, $2, $3
);
-- name: GetSessionAndUser :one
SELECT users.id, users.email, users.name, users.bio, sessions.expires_at
FROM sessions
JOIN users ON users.id = sessions.user_id
WHERE sessions.token_hash = $1 AND sessions.expires_at > now();
-- name: TouchSession :exec
UPDATE sessions
SET last_seen_at = now()
WHERE token_hash = $1;
-- name: DeleteSession :exec
DELETE FROM sessions
WHERE token_hash = $1;
-- name: CreatePlatform :one
INSERT INTO platform (
name
) VALUES (
$1
)
RETURNING id, name;
-- name: GetPlatform :one
SELECT id, name
FROM platform
WHERE id = $1 AND deleted_at IS NULL;
-- name: ListPlatforms :many
SELECT id, name
FROM platform
WHERE deleted_at IS NULL
ORDER BY name;
-- name: UpdatePlatform :one
UPDATE platform
SET name = $2
WHERE id = $1 AND deleted_at IS NULL
RETURNING id, name;
-- name: DeletePlatform :execrows
-- Soft delete, so the FK on game.platform_id is never touched and never
-- blocks this on its own; the NOT EXISTS guard is what stops a platform
-- that still has active games - from any user, since the list is shared -
-- from being deleted. Call GetPlatform first to tell "not found" apart from
-- "blocked by games" when this affects 0 rows.
UPDATE platform
SET deleted_at = now()
WHERE platform.id = $1 AND platform.deleted_at IS NULL
AND NOT EXISTS (
SELECT 1 FROM game
WHERE game.platform_id = platform.id AND game.deleted_at IS NULL
);
-- name: CreateGame :one
WITH inserted AS (
INSERT INTO game (
user_id, platform_id, name, score, release_date, finished
) VALUES (
$1, $2, $3, $4, $5, $6
)
RETURNING id, platform_id, name, score, release_date, finished
)
SELECT inserted.id, inserted.name, inserted.platform_id, platform.name AS platform_name,
inserted.score, inserted.release_date, inserted.finished
FROM inserted
JOIN platform ON platform.id = inserted.platform_id;
-- name: GetGame :one
SELECT game.id, game.name, game.platform_id, platform.name AS platform_name,
game.score, game.release_date, game.finished
FROM game
JOIN platform ON platform.id = game.platform_id
WHERE game.id = $1 AND game.user_id = $2 AND game.deleted_at IS NULL;
-- name: ListGames :many
SELECT game.id, game.name, game.platform_id, platform.name AS platform_name,
game.score, game.release_date, game.finished
FROM game
JOIN platform ON platform.id = game.platform_id
WHERE game.user_id = $1 AND game.deleted_at IS NULL
ORDER BY game.name;
-- name: UpdateGame :one
WITH updated AS (
UPDATE game
SET platform_id = $3, name = $4, score = $5, release_date = $6, finished = $7
WHERE game.id = $1 AND game.user_id = $2 AND game.deleted_at IS NULL
RETURNING id, platform_id, name, score, release_date, finished
)
SELECT updated.id, updated.name, updated.platform_id, platform.name AS platform_name,
updated.score, updated.release_date, updated.finished
FROM updated
JOIN platform ON platform.id = updated.platform_id;
-- name: DeleteGame :execrows
UPDATE game
SET deleted_at = now()
WHERE id = $1 AND user_id = $2 AND deleted_at IS NULL;