chore: release v0.26.4
This commit is contained in:
+27
-6
@@ -147,7 +147,17 @@ func NowISO() string { return time.Now().UTC().Format("2006-01-02T15:04:05.000Z"
|
||||
|
||||
// Open opens (and creates if needed) the SQLite database at the given path,
|
||||
// enables performance PRAGMAs, and applies embedded migrations.
|
||||
func Open(path string) (*sql.DB, error) {
|
||||
//
|
||||
// This is the SETTINGS database, which gets the full schema — see roles.go for
|
||||
// why. Use OpenLogbook for a database that holds contacts only.
|
||||
func Open(path string) (*sql.DB, error) { return open(path, RoleAll) }
|
||||
|
||||
// OpenLogbook opens a SQLite logbook: the qso table and nothing from the
|
||||
// settings side, and any unused settings tables an earlier version left in it
|
||||
// are dropped if they are empty.
|
||||
func OpenLogbook(path string) (*sql.DB, error) { return open(path, RoleLogbook) }
|
||||
|
||||
func open(path string, role Role) (*sql.DB, error) {
|
||||
// Escape only the two characters a path could contain that the DSN would
|
||||
// otherwise read as its query/fragment delimiters. Windows separators
|
||||
// (\\ and the drive ':') are left intact — url.PathEscape would mangle them.
|
||||
@@ -162,10 +172,12 @@ func Open(path string) (*sql.DB, error) {
|
||||
return nil, fmt.Errorf("ping sqlite: %w", err)
|
||||
}
|
||||
Dialect = "sqlite"
|
||||
if err := migrate(conn, nil, path, filepath.Base(path)); err != nil {
|
||||
label := filepath.Base(path)
|
||||
if err := migrate(conn, nil, path, label, role); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
pruneForeignTables(conn, role, label)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@@ -236,7 +248,7 @@ func backupBeforeRewrite(conn *sql.DB, dbPath, migration string) {
|
||||
// interleaved migration runs in one log were indistinguishable — an operator
|
||||
// reported "migrations are very slow" and the lines gave no way to tell one
|
||||
// database migrated three times from three databases migrated once.
|
||||
func migrate(conn *sql.DB, translate func(string) string, dbPath, label string) error {
|
||||
func migrate(conn *sql.DB, translate func(string) string, dbPath, label string, role Role) error {
|
||||
// A non-nil translator means this is the MySQL connection (use the
|
||||
// per-statement, FK-aware path); nil means a SQLite connection. This is
|
||||
// determined by the caller's argument, NOT the global Dialect, so the
|
||||
@@ -331,9 +343,18 @@ func migrate(conn *sql.DB, translate func(string) string, dbPath, label string)
|
||||
if err != nil {
|
||||
return fmt.Errorf("begin tx for %s: %w", name, err)
|
||||
}
|
||||
if _, err := tx.Exec(sqlText); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return fmt.Errorf("apply migration %s: %w", name, err)
|
||||
// Statement by statement rather than one Exec of the whole file: the role
|
||||
// filter works per statement, and a settings-table statement must not
|
||||
// reach a logbook database (see roles.go). Still one transaction, so the
|
||||
// file remains atomic.
|
||||
for _, stmt := range splitStatements(sqlText) {
|
||||
if !keepForRole(stmt, role) {
|
||||
continue
|
||||
}
|
||||
if _, err := tx.Exec(stmt); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return fmt.Errorf("apply migration %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
if _, err := tx.Exec(`INSERT INTO schema_migrations(name) VALUES(?)`, name); err != nil {
|
||||
_ = tx.Rollback()
|
||||
|
||||
Reference in New Issue
Block a user