feat(autobrrctl): add db migrate/seed/reset functionality (#934)

* add db seed/reset functionality

* speculative db migration support

* update postgresSchema

* refactor: only migrate data, no schema

* implement transaction in db migration function

* refactor db:seed and db:reset

added transaction to both and updated help message

* change table order

* skip on foreign key constraint violations

* skip feed_cache

* set constraints to null

* fix seed and reset

* simplify migrate func

This version of the function should behave similarly to the previous version, but with less repetition and by relying on PostgreSQL's automatic transaction handling, which starts a new transaction for each separate statement when not in a transaction block. Also, it prepares the insert statement only once for each table, which can significantly improve performance when there are many rows to insert.

* fixed release_action_status

* refactor db:seed and db:reset

* minor adjustment to const usage

* fixed usage examples

* refactor seed/migrate

* refactor(database): seed and convert

* refactor(database): make tables list sorted a-z

* chore(deps): go mod tidy

* refactor(database): convert improve log output

---------

Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
soup 2023-12-28 14:05:51 +01:00 committed by GitHub
parent 6a94ecacca
commit ebbd851a2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 327 additions and 8 deletions

View file

@ -0,0 +1,82 @@
package tools
import (
"database/sql"
"fmt"
"os"
"strings"
_ "modernc.org/sqlite"
)
type Seeder interface {
Reset() error
Seed() error
}
type SQLiteSeeder struct {
dbPath string
seedFile string
}
func NewSQLiteSeeder(dbPath, seedFile string) *SQLiteSeeder {
return &SQLiteSeeder{
dbPath: dbPath,
seedFile: seedFile,
}
}
func (s *SQLiteSeeder) Reset() error {
db, err := sql.Open("sqlite", s.dbPath)
if err != nil {
return fmt.Errorf("failed to open %s database: %v", "sqlite", err)
}
defer db.Close()
tables := GetTables()
for _, table := range tables {
if err := s.resetTable(db, table); err != nil {
return err
}
}
return nil
}
func (s *SQLiteSeeder) resetTable(db *sql.DB, table string) error {
if _, err := db.Exec("DELETE FROM ?", table); err != nil {
return fmt.Errorf("failed to delete rows from table %s: %v", table, err)
}
// Update sqlite_sequence, ignore errors for missing sqlite_sequence entry
if _, err := db.Exec("UPDATE sqlite_sequence SET seq = 0 WHERE name = ?", table); err != nil {
if !strings.Contains(err.Error(), "no such table") {
return fmt.Errorf("failed to reset primary key sequence for table %s: %v", table, err)
}
}
return nil
}
func (s *SQLiteSeeder) Seed() error {
sqlFile, err := os.ReadFile(s.seedFile)
if err != nil {
return fmt.Errorf("failed to read SQL file: %v", err)
}
db, err := sql.Open("sqlite", s.dbPath)
if err != nil {
return fmt.Errorf("failed to open %s database: %v", "sqlite", err)
}
defer db.Close()
sqlCommands := strings.Split(string(sqlFile), ";")
for _, cmd := range sqlCommands {
if _, err := db.Exec(cmd); err != nil {
return fmt.Errorf("failed to execute SQL command: %v", err)
}
}
return nil
}