refactor: deduplicate persistence, add upsert queries, throttle snake saves
Some checks failed
CI / Deploy / test (pull_request) Failing after 15s
CI / Deploy / lint (pull_request) Failing after 23s
CI / Deploy / deploy (pull_request) Has been skipped

- Replace Create+Get+Update with UpsertGame/UpsertSnakeGame queries
- Extract free functions (saveGame, loadGame, etc.) from duplicated
  receiver methods on Store and Instance types
- Remove duplicate generateID from snake package, reuse game.GenerateID
- Throttle snake game DB writes to every 2s instead of every tick
- Fix double-lock in c4game chat handler
- Update all code for sqlc pointer types (*string instead of sql.NullString)
This commit is contained in:
Ryan Hamamura
2026-03-02 16:56:29 -10:00
parent 9c3f659e96
commit bc6488f063
14 changed files with 318 additions and 494 deletions

View File

@@ -1,16 +1,18 @@
-- name: CreateGame :one
INSERT INTO games (id, board, current_turn, status)
VALUES (?, ?, ?, ?)
RETURNING *;
-- name: UpsertGame :exec
INSERT INTO games (id, board, current_turn, status, game_type, winner_user_id, winning_cells, rematch_game_id)
VALUES (?, ?, ?, ?, 'connect4', ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
board = excluded.board,
current_turn = excluded.current_turn,
status = excluded.status,
winner_user_id = excluded.winner_user_id,
winning_cells = excluded.winning_cells,
rematch_game_id = excluded.rematch_game_id,
updated_at = CURRENT_TIMESTAMP;
-- name: GetGame :one
SELECT * FROM games WHERE id = ?;
-- name: UpdateGame :exec
UPDATE games
SET board = ?, current_turn = ?, status = ?, winner_user_id = ?, winning_cells = ?, rematch_game_id = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: DeleteGame :exec
DELETE FROM games WHERE id = ?;

View File

@@ -1,16 +1,17 @@
-- name: CreateSnakeGame :one
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?)
RETURNING *;
-- name: UpsertSnakeGame :exec
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed, winner_user_id, rematch_game_id, score)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
board = excluded.board,
status = excluded.status,
winner_user_id = excluded.winner_user_id,
rematch_game_id = excluded.rematch_game_id,
score = excluded.score,
updated_at = CURRENT_TIMESTAMP;
-- name: GetSnakeGame :one
SELECT * FROM games WHERE id = ? AND game_type = 'snake';
-- name: UpdateSnakeGame :exec
UPDATE games
SET board = ?, status = ?, winner_user_id = ?, rematch_game_id = ?, score = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND game_type = 'snake';
-- name: DeleteSnakeGame :exec
DELETE FROM games WHERE id = ? AND game_type = 'snake';

View File

@@ -15,11 +15,11 @@ VALUES (?, ?, ?, ?, ?)
`
type CreateChatMessageParams struct {
GameID string
Nickname string
Color int64
Message string
CreatedAt int64
GameID string `db:"game_id" json:"game_id"`
Nickname string `db:"nickname" json:"nickname"`
Color int64 `db:"color" json:"color"`
Message string `db:"message" json:"message"`
CreatedAt int64 `db:"created_at" json:"created_at"`
}
func (q *Queries) CreateChatMessage(ctx context.Context, arg CreateChatMessageParams) error {
@@ -40,13 +40,13 @@ ORDER BY created_at DESC, id DESC
LIMIT 50
`
func (q *Queries) GetChatMessages(ctx context.Context, gameID string) ([]ChatMessage, error) {
func (q *Queries) GetChatMessages(ctx context.Context, gameID string) ([]*ChatMessage, error) {
rows, err := q.db.QueryContext(ctx, getChatMessages, gameID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ChatMessage
var items []*ChatMessage
for rows.Next() {
var i ChatMessage
if err := rows.Scan(
@@ -59,7 +59,7 @@ func (q *Queries) GetChatMessages(ctx context.Context, gameID string) ([]ChatMes
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err

View File

@@ -7,63 +7,21 @@ package repository
import (
"context"
"database/sql"
"time"
)
const createGame = `-- name: CreateGame :one
INSERT INTO games (id, board, current_turn, status)
VALUES (?, ?, ?, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
`
type CreateGameParams struct {
ID string
Board string
CurrentTurn int64
Status int64
}
func (q *Queries) CreateGame(ctx context.Context, arg CreateGameParams) (Game, error) {
row := q.db.QueryRowContext(ctx, createGame,
arg.ID,
arg.Board,
arg.CurrentTurn,
arg.Status,
)
var i Game
err := row.Scan(
&i.ID,
&i.Board,
&i.CurrentTurn,
&i.Status,
&i.WinnerUserID,
&i.WinningCells,
&i.CreatedAt,
&i.UpdatedAt,
&i.RematchGameID,
&i.GameType,
&i.GridWidth,
&i.GridHeight,
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
const createGamePlayer = `-- name: CreateGamePlayer :exec
INSERT INTO game_players (game_id, user_id, guest_player_id, nickname, color, slot)
VALUES (?, ?, ?, ?, ?, ?)
`
type CreateGamePlayerParams struct {
GameID string
UserID sql.NullString
GuestPlayerID sql.NullString
Nickname string
Color int64
Slot int64
GameID string `db:"game_id" json:"game_id"`
UserID *string `db:"user_id" json:"user_id"`
GuestPlayerID *string `db:"guest_player_id" json:"guest_player_id"`
Nickname string `db:"nickname" json:"nickname"`
Color int64 `db:"color" json:"color"`
Slot int64 `db:"slot" json:"slot"`
}
func (q *Queries) CreateGamePlayer(ctx context.Context, arg CreateGamePlayerParams) error {
@@ -91,13 +49,13 @@ const getActiveGames = `-- name: GetActiveGames :many
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'connect4' AND status < 2
`
func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
func (q *Queries) GetActiveGames(ctx context.Context) ([]*Game, error) {
rows, err := q.db.QueryContext(ctx, getActiveGames)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
var items []*Game
for rows.Next() {
var i Game
if err := rows.Scan(
@@ -120,7 +78,7 @@ func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -135,7 +93,7 @@ const getGame = `-- name: GetGame :one
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ?
`
func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
func (q *Queries) GetGame(ctx context.Context, id string) (*Game, error) {
row := q.db.QueryRowContext(ctx, getGame, id)
var i Game
err := row.Scan(
@@ -156,20 +114,20 @@ func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
&i.Score,
&i.SnakeSpeed,
)
return i, err
return &i, err
}
const getGamePlayers = `-- name: GetGamePlayers :many
SELECT game_id, user_id, guest_player_id, nickname, color, slot, created_at FROM game_players WHERE game_id = ?
`
func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]GamePlayer, error) {
func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]*GamePlayer, error) {
rows, err := q.db.QueryContext(ctx, getGamePlayers, gameID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GamePlayer
var items []*GamePlayer
for rows.Next() {
var i GamePlayer
if err := rows.Scan(
@@ -183,7 +141,7 @@ func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]GamePlay
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -201,13 +159,13 @@ WHERE gp.user_id = ?
ORDER BY g.updated_at DESC
`
func (q *Queries) GetGamesByUserID(ctx context.Context, userID sql.NullString) ([]Game, error) {
func (q *Queries) GetGamesByUserID(ctx context.Context, userID *string) ([]*Game, error) {
rows, err := q.db.QueryContext(ctx, getGamesByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
var items []*Game
for rows.Next() {
var i Game
if err := rows.Scan(
@@ -230,7 +188,7 @@ func (q *Queries) GetGamesByUserID(ctx context.Context, userID sql.NullString) (
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -257,21 +215,21 @@ ORDER BY g.updated_at DESC
`
type GetUserActiveGamesRow struct {
ID string
Status int64
CurrentTurn int64
UpdatedAt sql.NullTime
MyColor int64
OpponentNickname sql.NullString
ID string `db:"id" json:"id"`
Status int64 `db:"status" json:"status"`
CurrentTurn int64 `db:"current_turn" json:"current_turn"`
UpdatedAt *time.Time `db:"updated_at" json:"updated_at"`
MyColor int64 `db:"my_color" json:"my_color"`
OpponentNickname *string `db:"opponent_nickname" json:"opponent_nickname"`
}
func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString) ([]GetUserActiveGamesRow, error) {
func (q *Queries) GetUserActiveGames(ctx context.Context, userID *string) ([]*GetUserActiveGamesRow, error) {
rows, err := q.db.QueryContext(ctx, getUserActiveGames, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserActiveGamesRow
var items []*GetUserActiveGamesRow
for rows.Next() {
var i GetUserActiveGamesRow
if err := rows.Scan(
@@ -284,7 +242,7 @@ func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString)
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -295,31 +253,38 @@ func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString)
return items, nil
}
const updateGame = `-- name: UpdateGame :exec
UPDATE games
SET board = ?, current_turn = ?, status = ?, winner_user_id = ?, winning_cells = ?, rematch_game_id = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
const upsertGame = `-- name: UpsertGame :exec
INSERT INTO games (id, board, current_turn, status, game_type, winner_user_id, winning_cells, rematch_game_id)
VALUES (?, ?, ?, ?, 'connect4', ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
board = excluded.board,
current_turn = excluded.current_turn,
status = excluded.status,
winner_user_id = excluded.winner_user_id,
winning_cells = excluded.winning_cells,
rematch_game_id = excluded.rematch_game_id,
updated_at = CURRENT_TIMESTAMP
`
type UpdateGameParams struct {
Board string
CurrentTurn int64
Status int64
WinnerUserID sql.NullString
WinningCells sql.NullString
RematchGameID sql.NullString
ID string
type UpsertGameParams struct {
ID string `db:"id" json:"id"`
Board string `db:"board" json:"board"`
CurrentTurn int64 `db:"current_turn" json:"current_turn"`
Status int64 `db:"status" json:"status"`
WinnerUserID *string `db:"winner_user_id" json:"winner_user_id"`
WinningCells *string `db:"winning_cells" json:"winning_cells"`
RematchGameID *string `db:"rematch_game_id" json:"rematch_game_id"`
}
func (q *Queries) UpdateGame(ctx context.Context, arg UpdateGameParams) error {
_, err := q.db.ExecContext(ctx, updateGame,
func (q *Queries) UpsertGame(ctx context.Context, arg UpsertGameParams) error {
_, err := q.db.ExecContext(ctx, upsertGame,
arg.ID,
arg.Board,
arg.CurrentTurn,
arg.Status,
arg.WinnerUserID,
arg.WinningCells,
arg.RematchGameID,
arg.ID,
)
return err
}

View File

@@ -5,50 +5,56 @@
package repository
import (
"database/sql"
"time"
)
type ChatMessage struct {
ID int64
GameID string
Nickname string
Color int64
Message string
CreatedAt int64
ID int64 `db:"id" json:"id"`
GameID string `db:"game_id" json:"game_id"`
Nickname string `db:"nickname" json:"nickname"`
Color int64 `db:"color" json:"color"`
Message string `db:"message" json:"message"`
CreatedAt int64 `db:"created_at" json:"created_at"`
}
type Game struct {
ID string
Board string
CurrentTurn int64
Status int64
WinnerUserID sql.NullString
WinningCells sql.NullString
CreatedAt sql.NullTime
UpdatedAt sql.NullTime
RematchGameID sql.NullString
GameType string
GridWidth sql.NullInt64
GridHeight sql.NullInt64
MaxPlayers int64
GameMode int64
Score int64
SnakeSpeed int64
ID string `db:"id" json:"id"`
Board string `db:"board" json:"board"`
CurrentTurn int64 `db:"current_turn" json:"current_turn"`
Status int64 `db:"status" json:"status"`
WinnerUserID *string `db:"winner_user_id" json:"winner_user_id"`
WinningCells *string `db:"winning_cells" json:"winning_cells"`
CreatedAt *time.Time `db:"created_at" json:"created_at"`
UpdatedAt *time.Time `db:"updated_at" json:"updated_at"`
RematchGameID *string `db:"rematch_game_id" json:"rematch_game_id"`
GameType string `db:"game_type" json:"game_type"`
GridWidth *int64 `db:"grid_width" json:"grid_width"`
GridHeight *int64 `db:"grid_height" json:"grid_height"`
MaxPlayers int64 `db:"max_players" json:"max_players"`
GameMode int64 `db:"game_mode" json:"game_mode"`
Score int64 `db:"score" json:"score"`
SnakeSpeed int64 `db:"snake_speed" json:"snake_speed"`
}
type GamePlayer struct {
GameID string
UserID sql.NullString
GuestPlayerID sql.NullString
Nickname string
Color int64
Slot int64
CreatedAt sql.NullTime
GameID string `db:"game_id" json:"game_id"`
UserID *string `db:"user_id" json:"user_id"`
GuestPlayerID *string `db:"guest_player_id" json:"guest_player_id"`
Nickname string `db:"nickname" json:"nickname"`
Color int64 `db:"color" json:"color"`
Slot int64 `db:"slot" json:"slot"`
CreatedAt *time.Time `db:"created_at" json:"created_at"`
}
type Session struct {
Token string `db:"token" json:"token"`
Data []byte `db:"data" json:"data"`
Expiry float64 `db:"expiry" json:"expiry"`
}
type User struct {
ID string
Username string
PasswordHash string
CreatedAt sql.NullTime
ID string `db:"id" json:"id"`
Username string `db:"username" json:"username"`
PasswordHash string `db:"password_hash" json:"password_hash"`
CreatedAt *time.Time `db:"created_at" json:"created_at"`
}

View File

@@ -7,69 +7,21 @@ package repository
import (
"context"
"database/sql"
"time"
)
const createSnakeGame = `-- name: CreateSnakeGame :one
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
`
type CreateSnakeGameParams struct {
ID string
Board string
Status int64
GridWidth sql.NullInt64
GridHeight sql.NullInt64
GameMode int64
SnakeSpeed int64
}
func (q *Queries) CreateSnakeGame(ctx context.Context, arg CreateSnakeGameParams) (Game, error) {
row := q.db.QueryRowContext(ctx, createSnakeGame,
arg.ID,
arg.Board,
arg.Status,
arg.GridWidth,
arg.GridHeight,
arg.GameMode,
arg.SnakeSpeed,
)
var i Game
err := row.Scan(
&i.ID,
&i.Board,
&i.CurrentTurn,
&i.Status,
&i.WinnerUserID,
&i.WinningCells,
&i.CreatedAt,
&i.UpdatedAt,
&i.RematchGameID,
&i.GameType,
&i.GridWidth,
&i.GridHeight,
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
const createSnakePlayer = `-- name: CreateSnakePlayer :exec
INSERT INTO game_players (game_id, user_id, guest_player_id, nickname, color, slot)
VALUES (?, ?, ?, ?, ?, ?)
`
type CreateSnakePlayerParams struct {
GameID string
UserID sql.NullString
GuestPlayerID sql.NullString
Nickname string
Color int64
Slot int64
GameID string `db:"game_id" json:"game_id"`
UserID *string `db:"user_id" json:"user_id"`
GuestPlayerID *string `db:"guest_player_id" json:"guest_player_id"`
Nickname string `db:"nickname" json:"nickname"`
Color int64 `db:"color" json:"color"`
Slot int64 `db:"slot" json:"slot"`
}
func (q *Queries) CreateSnakePlayer(ctx context.Context, arg CreateSnakePlayerParams) error {
@@ -97,13 +49,13 @@ const getActiveSnakeGames = `-- name: GetActiveSnakeGames :many
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'snake' AND status < 2 AND game_mode = 0
`
func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]*Game, error) {
rows, err := q.db.QueryContext(ctx, getActiveSnakeGames)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
var items []*Game
for rows.Next() {
var i Game
if err := rows.Scan(
@@ -126,7 +78,7 @@ func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -141,7 +93,7 @@ const getSnakeGame = `-- name: GetSnakeGame :one
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ? AND game_type = 'snake'
`
func (q *Queries) GetSnakeGame(ctx context.Context, id string) (Game, error) {
func (q *Queries) GetSnakeGame(ctx context.Context, id string) (*Game, error) {
row := q.db.QueryRowContext(ctx, getSnakeGame, id)
var i Game
err := row.Scan(
@@ -162,20 +114,20 @@ func (q *Queries) GetSnakeGame(ctx context.Context, id string) (Game, error) {
&i.Score,
&i.SnakeSpeed,
)
return i, err
return &i, err
}
const getSnakePlayers = `-- name: GetSnakePlayers :many
SELECT game_id, user_id, guest_player_id, nickname, color, slot, created_at FROM game_players WHERE game_id = ? ORDER BY slot
`
func (q *Queries) GetSnakePlayers(ctx context.Context, gameID string) ([]GamePlayer, error) {
func (q *Queries) GetSnakePlayers(ctx context.Context, gameID string) ([]*GamePlayer, error) {
rows, err := q.db.QueryContext(ctx, getSnakePlayers, gameID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GamePlayer
var items []*GamePlayer
for rows.Next() {
var i GamePlayer
if err := rows.Scan(
@@ -189,7 +141,7 @@ func (q *Queries) GetSnakePlayers(ctx context.Context, gameID string) ([]GamePla
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -214,20 +166,20 @@ ORDER BY g.updated_at DESC
`
type GetUserActiveSnakeGamesRow struct {
ID string
Status int64
GridWidth sql.NullInt64
GridHeight sql.NullInt64
UpdatedAt sql.NullTime
ID string `db:"id" json:"id"`
Status int64 `db:"status" json:"status"`
GridWidth *int64 `db:"grid_width" json:"grid_width"`
GridHeight *int64 `db:"grid_height" json:"grid_height"`
UpdatedAt *time.Time `db:"updated_at" json:"updated_at"`
}
func (q *Queries) GetUserActiveSnakeGames(ctx context.Context, userID sql.NullString) ([]GetUserActiveSnakeGamesRow, error) {
func (q *Queries) GetUserActiveSnakeGames(ctx context.Context, userID *string) ([]*GetUserActiveSnakeGamesRow, error) {
rows, err := q.db.QueryContext(ctx, getUserActiveSnakeGames, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserActiveSnakeGamesRow
var items []*GetUserActiveSnakeGamesRow
for rows.Next() {
var i GetUserActiveSnakeGamesRow
if err := rows.Scan(
@@ -239,7 +191,7 @@ func (q *Queries) GetUserActiveSnakeGames(ctx context.Context, userID sql.NullSt
); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, &i)
}
if err := rows.Close(); err != nil {
return nil, err
@@ -250,29 +202,43 @@ func (q *Queries) GetUserActiveSnakeGames(ctx context.Context, userID sql.NullSt
return items, nil
}
const updateSnakeGame = `-- name: UpdateSnakeGame :exec
UPDATE games
SET board = ?, status = ?, winner_user_id = ?, rematch_game_id = ?, score = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND game_type = 'snake'
const upsertSnakeGame = `-- name: UpsertSnakeGame :exec
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed, winner_user_id, rematch_game_id, score)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
board = excluded.board,
status = excluded.status,
winner_user_id = excluded.winner_user_id,
rematch_game_id = excluded.rematch_game_id,
score = excluded.score,
updated_at = CURRENT_TIMESTAMP
`
type UpdateSnakeGameParams struct {
Board string
Status int64
WinnerUserID sql.NullString
RematchGameID sql.NullString
Score int64
ID string
type UpsertSnakeGameParams struct {
ID string `db:"id" json:"id"`
Board string `db:"board" json:"board"`
Status int64 `db:"status" json:"status"`
GridWidth *int64 `db:"grid_width" json:"grid_width"`
GridHeight *int64 `db:"grid_height" json:"grid_height"`
GameMode int64 `db:"game_mode" json:"game_mode"`
SnakeSpeed int64 `db:"snake_speed" json:"snake_speed"`
WinnerUserID *string `db:"winner_user_id" json:"winner_user_id"`
RematchGameID *string `db:"rematch_game_id" json:"rematch_game_id"`
Score int64 `db:"score" json:"score"`
}
func (q *Queries) UpdateSnakeGame(ctx context.Context, arg UpdateSnakeGameParams) error {
_, err := q.db.ExecContext(ctx, updateSnakeGame,
func (q *Queries) UpsertSnakeGame(ctx context.Context, arg UpsertSnakeGameParams) error {
_, err := q.db.ExecContext(ctx, upsertSnakeGame,
arg.ID,
arg.Board,
arg.Status,
arg.GridWidth,
arg.GridHeight,
arg.GameMode,
arg.SnakeSpeed,
arg.WinnerUserID,
arg.RematchGameID,
arg.Score,
arg.ID,
)
return err
}

View File

@@ -16,12 +16,12 @@ RETURNING id, username, password_hash, created_at
`
type CreateUserParams struct {
ID string
Username string
PasswordHash string
ID string `db:"id" json:"id"`
Username string `db:"username" json:"username"`
PasswordHash string `db:"password_hash" json:"password_hash"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (*User, error) {
row := q.db.QueryRowContext(ctx, createUser, arg.ID, arg.Username, arg.PasswordHash)
var i User
err := row.Scan(
@@ -30,14 +30,14 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
&i.PasswordHash,
&i.CreatedAt,
)
return i, err
return &i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT id, username, password_hash, created_at FROM users WHERE id = ?
`
func (q *Queries) GetUserByID(ctx context.Context, id string) (User, error) {
func (q *Queries) GetUserByID(ctx context.Context, id string) (*User, error) {
row := q.db.QueryRowContext(ctx, getUserByID, id)
var i User
err := row.Scan(
@@ -46,14 +46,14 @@ func (q *Queries) GetUserByID(ctx context.Context, id string) (User, error) {
&i.PasswordHash,
&i.CreatedAt,
)
return i, err
return &i, err
}
const getUserByUsername = `-- name: GetUserByUsername :one
SELECT id, username, password_hash, created_at FROM users WHERE username = ?
`
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (*User, error) {
row := q.db.QueryRowContext(ctx, getUserByUsername, username)
var i User
err := row.Scan(
@@ -62,5 +62,5 @@ func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User,
&i.PasswordHash,
&i.CreatedAt,
)
return i, err
return &i, err
}