package db import ( "context" "errors" "fmt" "time" "github.com/jmoiron/sqlx" "golang.org/x/sync/errgroup" ) var ( ErrNoQuery = errors.New("Query has not been set") ErrorRecordNotFound = errors.New("Record not found") ) type AllowedIds interface { ~uint | ~string } type InsertId[T AllowedIds] struct { id T fieldName string tableName string query string } type Insertable[T AllowedIds] interface { Query(data *InsertId[T]) Insert(tx *sqlx.Tx, relId *InsertId[T]) error InsertCtx(ctx context.Context, tx *sqlx.Tx, relId *InsertId[T]) error } func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId InsertId[T]) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() data.Query(&relId) return data.InsertCtx(ctx, tx, &relId) } func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, ctx context.Context, tx *sqlx.Tx, data I, relId InsertId[T]) { g.Go(func() error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() data.Query(&relId) err := data.InsertCtx(ctx, tx, &relId) return err }) } func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId InsertId[T]) error { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() if len(data) > 0 { data[0].Query(&relId) } for i := range data { err := data[i].InsertCtx(ctx, tx, &relId) if err != nil { return err } } return nil } func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T]) { g.Go(func() error { wg, _ := errgroup.WithContext(context.Background()) var err error if len(data) > 0 { data[0].Query(&relId) } for i := range data { wg.Go(func() error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err = data[i].InsertCtx(ctx, tx, &relId) return err }) } return wg.Wait() }) } func InsertSimpleSlice[T AllowedIds, I any](tx *sqlx.Tx, data []I, relId *InsertId[T]) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := fmt.Sprintf(` INSERT INTO %s (%s, value) VALUES ($1, $2)`, relId.tableName, relId.fieldName) for i := range data { _, err := tx.ExecContext(ctx, query, relId.id, data[i]) if err != nil { return err } } return nil } func InsertSimpleSliceParallel[T AllowedIds, I any](g *errgroup.Group, tx *sqlx.Tx, data []I, relId *InsertId[T]) { g.Go(func() error { wg, _ := errgroup.WithContext(context.Background()) query := fmt.Sprintf(` INSERT INTO %s (%s, value) VALUES ($1, $2)`, relId.tableName, relId.fieldName) for i := range data { wg.Go(func() error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() _, err := tx.ExecContext(ctx, query, relId.id, data[i]) return err }) } return wg.Wait() }) } func DeleteAll(db *sqlx.DB) error { query := ` DELETE FROM setup` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() result, err := db.ExecContext(ctx, query) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrorRecordNotFound } return nil } func DeleteAllSeq(db *sqlx.DB) error { query := ` DELETE FROM sqlite_sequence` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() result, err := db.ExecContext(ctx, query) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrorRecordNotFound } return nil }