Add database versioning
This commit is contained in:
parent
0de9b25dfe
commit
5f97014bdc
@ -82,6 +82,7 @@ func NewProfile(target_dir string) (Profile, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return Profile{}, err
|
return Profile{}, err
|
||||||
}
|
}
|
||||||
|
InitializeDatabaseVersion(db)
|
||||||
|
|
||||||
// Create `users.txt`
|
// Create `users.txt`
|
||||||
fmt.Printf("Creating............. %s\n", user_list_file)
|
fmt.Printf("Creating............. %s\n", user_list_file)
|
||||||
@ -183,10 +184,13 @@ func LoadProfile(profile_dir string) (Profile, error) {
|
|||||||
return Profile{}, err
|
return Profile{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Profile{
|
ret := Profile{
|
||||||
ProfileDir: profile_dir,
|
ProfileDir: profile_dir,
|
||||||
UsersList: users_list,
|
UsersList: users_list,
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
DB: db,
|
DB: db,
|
||||||
}, nil
|
}
|
||||||
|
err = ret.check_and_update_version()
|
||||||
|
|
||||||
|
return ret, err
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,15 @@ func TestNewProfile(t *testing.T) {
|
|||||||
t.Fatalf("Expected `%s` to be a %s, but got %s [%s]", v.filename, isdir_map(v.isDir), contents[i].Name(), isdir_map(contents[i].IsDir()))
|
t.Fatalf("Expected `%s` to be a %s, but got %s [%s]", v.filename, isdir_map(v.isDir), contents[i].Name(), isdir_map(contents[i].IsDir()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check database version is initialized
|
||||||
|
version, err := profile.GetDatabaseVersion()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if version != 0 {
|
||||||
|
t.Errorf("Expected database version %d, but got %d", 0, version)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,3 +113,7 @@ create table hashtags (rowid integer primary key,
|
|||||||
unique (tweet_id, text)
|
unique (tweet_id, text)
|
||||||
foreign key(tweet_id) references tweets(id)
|
foreign key(tweet_id) references tweets(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table database_version(rowid integer primary key,
|
||||||
|
version_number integer not null unique
|
||||||
|
);
|
||||||
|
90
persistence/versions.go
Normal file
90
persistence/versions.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const ENGINE_DATABASE_VERSION = 0
|
||||||
|
|
||||||
|
|
||||||
|
type VersionMismatchError struct {
|
||||||
|
EngineVersion int
|
||||||
|
DatabaseVersion int
|
||||||
|
}
|
||||||
|
func (e VersionMismatchError) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`This profile was created with database schema version %d, which is newer than this application's database schema version, %d.
|
||||||
|
Please upgrade this application to a newer version to use this profile. Or downgrade the profile's schema version, somehow.`,
|
||||||
|
e.DatabaseVersion, e.EngineVersion,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Nth entry is the migration that moves you from version N to version N+1
|
||||||
|
*/
|
||||||
|
var MIGRATIONS = []string{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should only get called on a newly created Profile.
|
||||||
|
* Subsequent updates should change the number, not insert a new row.
|
||||||
|
*/
|
||||||
|
func InitializeDatabaseVersion(db *sql.DB) {
|
||||||
|
_, err := db.Exec("insert into database_version (version_number) values (?)", ENGINE_DATABASE_VERSION)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Profile) GetDatabaseVersion() (int, error) {
|
||||||
|
row := p.DB.QueryRow("select version_number from database_version")
|
||||||
|
|
||||||
|
var version int
|
||||||
|
|
||||||
|
err := row.Scan(&version)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Profile) check_and_update_version() error {
|
||||||
|
version, err := p.GetDatabaseVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if version > ENGINE_DATABASE_VERSION {
|
||||||
|
return VersionMismatchError{ENGINE_DATABASE_VERSION, version}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ENGINE_DATABASE_VERSION > version {
|
||||||
|
fmt.Printf("Upgrading database from version %d to version %d", version, ENGINE_DATABASE_VERSION)
|
||||||
|
p.UpgradeFromXToY(version, ENGINE_DATABASE_VERSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all the migrations from version X to version Y, and update the `database_version` table's `version_number`
|
||||||
|
*/
|
||||||
|
func (p Profile) UpgradeFromXToY(x int, y int) error {
|
||||||
|
for i := x; i < y; i++ {
|
||||||
|
fmt.Println(MIGRATIONS[i])
|
||||||
|
_, err := p.DB.Exec(MIGRATIONS[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = p.DB.Exec("update database_version set version_number = ?", i+1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Now at database schema version %d.\n", i + 1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
33
persistence/versions_test.go
Normal file
33
persistence/versions_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package persistence_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"os"
|
||||||
|
"offline_twitter/scraper"
|
||||||
|
"offline_twitter/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionUpgrade(t *testing.T) {
|
||||||
|
profile_path := "test_profiles/TestVersions"
|
||||||
|
if file_exists(profile_path) {
|
||||||
|
err := os.RemoveAll(profile_path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
profile := create_or_load_profile(profile_path)
|
||||||
|
|
||||||
|
test_migration := "insert into tweets (id, user_id, text) values (21250554358298342, -1, 'awefjk')"
|
||||||
|
test_tweet_id := scraper.TweetID(21250554358298342)
|
||||||
|
|
||||||
|
if profile.IsTweetInDatabase(test_tweet_id) {
|
||||||
|
t.Fatalf("Test tweet shouldn't be in the database yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
persistence.MIGRATIONS = append(persistence.MIGRATIONS, test_migration)
|
||||||
|
profile.UpgradeFromXToY(persistence.ENGINE_DATABASE_VERSION, persistence.ENGINE_DATABASE_VERSION + 1)
|
||||||
|
|
||||||
|
if !profile.IsTweetInDatabase(test_tweet_id) {
|
||||||
|
t.Errorf("Migration should have created the tweet, but it didn't")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user