package db import ( "context" "database/sql" "errors" "fmt" "time" "github.com/jmoiron/sqlx" "golang.org/x/sync/errgroup" ) type World struct { ID string Title string Description string Version string System string Background string JoinTheme string CoreVersion string SystemVersion string LastPlayed string Playtime int Availability int NextSession time.Time Socket bool Protected bool Exclusive bool PersistentStorage bool Locked bool Owned bool HasStorage bool Compatibility Compatibility Relationships Relationships Tags []string Scripts []string Esmodules []string Authors []*Author Media []*Media Styles []*Style Languages []*Language Packs []*Pack PackFolders []*Folder } func (w *World) Query(data *InsertId[string]) { data.query = fmt.Sprintf(` INSERT INTO world (%[1]s, id, title, description, version, system, background, join_theme, core_version, system_version, last_played, playtime, availability, next_session, socket, protected, exclusive_, persistent_storage, locked, owned, has_storage) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) ON CONFLICT(id) DO UPDATE SET %[1]s = EXCLUDED.%[1]s, updated_at = datetime('now') RETURNING (created_at == updated_at) AS was_inserted`, data.fieldName) } func (w *World) InsertObjects(tx *sqlx.Tx) error { relId := InsertId[string]{id: w.ID, fieldName: "world_id"} group, ctx := errgroup.WithContext(context.Background()) InsertWithCtxParallel(group, ctx, tx, w.Compatibility, relId) InsertWithCtxParallel(group, ctx, tx, w.Relationships, relId) esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"} InsertSimpleSliceParallel(group, tx, w.Esmodules, esModulesRelId) scriptRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "scripts"} InsertSimpleSliceParallel(group, tx, w.Scripts, scriptRelId) tagsRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "tags"} InsertSimpleSliceParallel(group, tx, w.Tags, tagsRelId) InsertSliceParallel(group, tx, w.Authors, relId) InsertSliceParallel(group, tx, w.Media, relId) InsertSliceParallel(group, tx, w.Styles, relId) InsertSliceParallel(group, tx, w.Languages, relId) InsertSliceParallel(group, tx, w.Packs, relId) InsertSliceParallel(group, tx, w.PackFolders, relId) err := group.Wait() if err != nil && !errors.Is(err, sql.ErrNoRows) { return err } return nil } func (w *World) Insert(tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme, w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket, w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage} var isInserted bool err := tx.QueryRowx(data.query, args...).Scan(&isInserted) if err != nil { return err } if isInserted { return w.InsertObjects(tx) } return nil } func (w *World) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme, w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket, w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage} var isInserted bool err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted) if err != nil { return err } if isInserted { return w.InsertObjects(tx) } return nil }