package db import ( "context" "database/sql" "errors" "strconv" "time" "github.com/jmoiron/sqlx" "golang.org/x/sync/errgroup" ) type Game struct { ID uint CreatedAt time.Time DemoMode bool IdleLogout bool Paused bool UserID string Addresses Addresses Files Files Options GameOptions Release Release World *World System *System CoreUpdate CoreUpdate SystemUpdate SystemUpdate ActiveUsers []string Modules []*Module PackageWarnings []*PackageWarning Packs []*Pack Messages []*Message Combats []*Combat CardDeck []*CardDeck Users []*User Macros []*Macro Folders []*WorldFolder Items []*Item Settings []*Setting Journals []*Journal Tables []*Table Playlists []*Playlist Actors []*Actor // Scenes []Scene } func (g *Game) InsertObjects(tx *sqlx.Tx) error { relData := InsertId[uint]{id: g.ID, fieldName: "game_id"} group, ctx := errgroup.WithContext(context.Background()) InsertWithCtxParallel(group, ctx, tx, &g.Addresses, relData) InsertWithCtxParallel(group, ctx, tx, &g.Files, relData) InsertWithCtxParallel(group, ctx, tx, &g.Options, relData) InsertWithCtxParallel(group, ctx, tx, &g.Release, relData) InsertWithCtxParallel(group, ctx, tx, &g.CoreUpdate, relData) InsertWithCtxParallel(group, ctx, tx, &g.SystemUpdate, relData) relDataString := InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"} InsertWithCtxParallel(group, ctx, tx, g.World, relDataString) InsertWithCtxParallel(group, ctx, tx, g.System, relDataString) InsertSimpleSliceParallel(group, tx, g.ActiveUsers, &InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"}) InsertSliceParallel(group, tx, g.Modules, relData) InsertSliceParallel(group, tx, g.PackageWarnings, relData) InsertSliceParallel(group, tx, g.Packs, relDataString) // InsertSliceParallel(group, tx, g.Messages, relData) // InsertSliceParallel(group, tx, g.Combats, relData) // InsertSliceParallel(group, tx, g.CardDeck, relData) // InsertSliceParallel(group, tx, g.Users, relData) // InsertSliceParallel(group, tx, g.Macros, relData) // InsertSliceParallel(group, tx, g.Folders, relData) // InsertSliceParallel(group, tx, g.Items, // InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"}) // InsertSliceParallel(group, tx, g.Settings, relData) // InsertSliceParallel(group, tx, g.Journals, relData) // InsertSliceParallel(group, tx, g.Tables, relData) // InsertSliceParallel(group, tx, g.Playlists, relData) // InsertSliceParallel(group, tx, g.Actors, relData) err := group.Wait() if err != nil && !errors.Is(err, sql.ErrNoRows) { return err } return nil } func (g *Game) Insert(db *sqlx.DB) error { tx := db.MustBegin() defer tx.Rollback() const query = ` INSERT INTO game (demo_mode, idle_logout, paused, user_id) VALUES ($1, $2, $3, $4) RETURNING id, created_at` args := []any{g.DemoMode, g.IdleLogout, g.Paused, g.UserID} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := tx.QueryRowxContext(ctx, query, args...).Scan(&g.ID, &g.CreatedAt) if err != nil { return err } err = g.InsertObjects(tx) if err != nil { return err } return tx.Commit() }