97 lines
2.0 KiB
Go
97 lines
2.0 KiB
Go
//go:build cgo
|
|
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
func BackupSQLite(dstPath string) (err error) {
|
|
if IsPostgres() {
|
|
return errors.New("sqlite backup is unavailable for PostgreSQL")
|
|
}
|
|
if db == nil {
|
|
return errors.New("database is not initialized")
|
|
}
|
|
if _, err := os.Lstat(dstPath); err == nil {
|
|
return fmt.Errorf("sqlite backup destination already exists: %s", dstPath)
|
|
} else if !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
_ = os.Remove(dstPath)
|
|
}
|
|
}()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), backupSQLiteTimeout)
|
|
defer cancel()
|
|
|
|
sourceDB, err := db.DB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sourceConn, err := sourceDB.Conn(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sourceConn.Close()
|
|
|
|
destinationDB, err := sql.Open("sqlite3", dstPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer destinationDB.Close()
|
|
destinationConn, err := destinationDB.Conn(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer destinationConn.Close()
|
|
|
|
return sourceConn.Raw(func(sourceDriver any) error {
|
|
source, ok := sourceDriver.(*sqlite3.SQLiteConn)
|
|
if !ok {
|
|
return fmt.Errorf("unexpected SQLite source connection type %T", sourceDriver)
|
|
}
|
|
return destinationConn.Raw(func(destinationDriver any) error {
|
|
destination, ok := destinationDriver.(*sqlite3.SQLiteConn)
|
|
if !ok {
|
|
return fmt.Errorf("unexpected SQLite destination connection type %T", destinationDriver)
|
|
}
|
|
backup, err := destination.Backup("main", source, "main")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
finished := false
|
|
defer func() {
|
|
if !finished {
|
|
_ = backup.Finish()
|
|
}
|
|
}()
|
|
for {
|
|
done, err := backup.Step(backupSQLiteStepPages())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if done {
|
|
finished = true
|
|
return backup.Finish()
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|