package db import ( "context" "encoding/json" "fmt" "github.com/BattlesnakeOfficial/rules/board" "github.com/jackc/pgx/v5/pgxpool" ) // the db package provides a simple interface to interact with a PostgreSQL database, it is extremely simple and only writing, reading and deleting game info // @ author Bhavnoor Singh Saroya // Database wraps pgxpool type Database struct { Pool *pgxpool.Pool } // Connect initializes the pool and creates the table if needed func Connect(dsn string) (*Database, error) { ctx := context.Background() pool, err := pgxpool.New(ctx, dsn) if err != nil { return nil, fmt.Errorf("db connect: %w", err) } // Ensure table exists _, err = pool.Exec(ctx, ` CREATE TABLE IF NOT EXISTS items ( id UUID PRIMARY KEY, info JSONB NOT NULL, frames JSONB[] ) `) if err != nil { return nil, fmt.Errorf("create table: %w", err) } return &Database{Pool: pool}, nil } func (db *Database) GetInfo(ctx context.Context, id string) (*board.Game, error) { var gameData []byte var it board.Game err := db.Pool.QueryRow(ctx, "SELECT info FROM items WHERE id=$1", id). Scan(&gameData) if err != nil { return nil, err } // 2. Unmarshal the byte slice into the struct. err = json.Unmarshal(gameData, &it) if err != nil { return nil, err } return &it, nil } func (db *Database) WriteInfo(ctx context.Context, info board.Game, frames []board.GameEvent) error { var err error var uuid string = info.ID // 1. Marshal the struct into a byte slice. gameData, err := json.Marshal(info) if err != nil { return fmt.Errorf("marshal game info: %w", err) } // convert []board.GameEvent to []json.RawMessage var framesData []json.RawMessage for _, f := range frames { b, err := json.Marshal(f) if err != nil { return fmt.Errorf("marshal frame: %w", err) } framesData = append(framesData, b) } // use pgtype to wrap into postgres []JSONB _, errr := db.Pool.Exec(ctx, "INSERT INTO items (id, info, frames) VALUES ($1, $2, $3)", uuid, gameData, framesData) if errr != nil { print("error writing to db, bad stuff happened: ") // panic(fmt.Sprintf("Error inserting item: %v", err)) return fmt.Errorf("insert item: %w", errr) } return nil } func (db *Database) Delete(ctx context.Context, id int) error { _, err := db.Pool.Exec(ctx, "DELETE FROM items WHERE id=$1", id) return err }