From 51519b62dd9f77b18bce96b4c548558c0d445f3f Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Sat, 8 Mar 2014 12:19:28 +0700 Subject: [PATCH 01/17] Actually use --driver option to set database driver for drone. This includes refactoring database setup, and migration system. Remove setupDatabase from main and use `Init` method from database package. At database package, defines Init method which actually initiate database with options given from comand line flag. I think `--path` wont be used anywhere so I plan to remove it later. Both meddler and migration initiated here, then we call `Set` method to setup all the tables, etc. Here I think I want to separate database schema and turn it into migration script instead, later maybe. At migration package I made some tweak to `Operation` interface. Realized that it's ludicrous to let migration driver re-implement `Exec` and `Query`, I made migration script to receive The whole migrationDriver struct which contains both Operation implementor, and the Tx itself. This made possible thanks to Go struct being able to promote its member, now our migration is more transparent. There's also stub implementation for bot mysql and postgresql, will implement this really soon. --- Makefile | 2 + cmd/droned/drone.go | 27 +------ pkg/database/database.go | 41 ++++++++++ ...402200603_rename_privelege_to_privilege.go | 8 +- .../201402211147_github_enterprise_support.go | 14 ++-- pkg/database/migrate/migrate.go | 23 +++--- pkg/database/migrate/mysql.go | 41 ++++++++++ pkg/database/migrate/postgresql.go | 41 ++++++++++ pkg/database/migrate/sqlite.go | 37 ++++----- pkg/database/migrate/sqlite_test.go | 78 +++++++++---------- 10 files changed, 203 insertions(+), 109 deletions(-) create mode 100644 pkg/database/migrate/mysql.go create mode 100644 pkg/database/migrate/postgresql.go diff --git a/Makefile b/Makefile index a4058a5f1..23aaef08b 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ deps: go get github.com/drone/go-bitbucket/bitbucket go get github.com/GeertJohan/go.rice go get github.com/GeertJohan/go.rice/rice + go get github.com/go-sql-driver/mysql + go get github.com/lib/pq go get github.com/mattn/go-sqlite3 go get github.com/russross/meddler diff --git a/cmd/droned/drone.go b/cmd/droned/drone.go index a2a9b86ab..3cad9032d 100644 --- a/cmd/droned/drone.go +++ b/cmd/droned/drone.go @@ -1,7 +1,6 @@ package main import ( - "database/sql" "flag" "log" "net/http" @@ -12,13 +11,10 @@ import ( "code.google.com/p/go.net/websocket" "github.com/GeertJohan/go.rice" "github.com/bmizerany/pat" - _ "github.com/mattn/go-sqlite3" - "github.com/russross/meddler" "github.com/drone/drone/pkg/build/docker" "github.com/drone/drone/pkg/channel" "github.com/drone/drone/pkg/database" - "github.com/drone/drone/pkg/database/migrate" "github.com/drone/drone/pkg/handler" "github.com/drone/drone/pkg/queue" ) @@ -66,7 +62,9 @@ func main() { checkTLSFlags() // setup database and handlers - setupDatabase() + if err := database.Init(driver, datasource); err != nil { + log.Fatal("Can't initialize database:", err) + } setupStatic() setupHandlers() @@ -92,25 +90,6 @@ func checkTLSFlags() { } -// setup the database connection and register with the -// global database package. -func setupDatabase() { - // inform meddler and migration we're using sqlite - meddler.Default = meddler.SQLite - migrate.Driver = migrate.SQLite - - // connect to the SQLite database - db, err := sql.Open(driver, datasource) - if err != nil { - log.Fatal(err) - } - - database.Set(db) - - migration := migrate.New(db) - migration.All().Migrate() -} - // setup routes for static assets. These assets may // be directly embedded inside the application using // the `rice embed` command, else they are served from disk. diff --git a/pkg/database/database.go b/pkg/database/database.go index 223405433..fcaa3521c 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -4,12 +4,53 @@ import ( "database/sql" "log" + "github.com/drone/drone/pkg/database/migrate" "github.com/drone/drone/pkg/database/schema" + + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + + "github.com/russross/meddler" ) // global instance of our database connection. var db *sql.DB +func Init(name, datasource string) error { + driver := map[string]struct { + Md *meddler.Database + Mg migrate.DriverFunction + }{ + "sqlite3": { + meddler.SQLite, + migrate.SQLite, + }, + "mysql": { + meddler.MySQL, + migrate.MySQL, + }, + "postgresql": { + meddler.PostgreSQL, + migrate.PostgreSQL, + }, + } + + meddler.Default = driver[name].Md + migrate.Driver = driver[name].Mg + + db, err := sql.Open(name, datasource) + if err != nil { + return err + } + + Set(db) + + migration := migrate.New(db) + migration.All().Migrate() + return nil +} + // Set sets the default database. func Set(database *sql.DB) { // set the global database diff --git a/pkg/database/migrate/201402200603_rename_privelege_to_privilege.go b/pkg/database/migrate/201402200603_rename_privelege_to_privilege.go index 379a6649e..eeb1cdb29 100644 --- a/pkg/database/migrate/201402200603_rename_privelege_to_privilege.go +++ b/pkg/database/migrate/201402200603_rename_privelege_to_privilege.go @@ -8,15 +8,15 @@ func (r *Rev1) Revision() int64 { return 201402200603 } -func (r *Rev1) Up(op Operation) error { - _, err := op.RenameColumns("repos", map[string]string{ +func (r *Rev1) Up(mg *MigrationDriver) error { + _, err := mg.RenameColumns("repos", map[string]string{ "priveleged": "privileged", }) return err } -func (r *Rev1) Down(op Operation) error { - _, err := op.RenameColumns("repos", map[string]string{ +func (r *Rev1) Down(mg *MigrationDriver) error { + _, err := mg.RenameColumns("repos", map[string]string{ "privileged": "priveleged", }) return err diff --git a/pkg/database/migrate/201402211147_github_enterprise_support.go b/pkg/database/migrate/201402211147_github_enterprise_support.go index dcdd5e8f1..8c252d782 100644 --- a/pkg/database/migrate/201402211147_github_enterprise_support.go +++ b/pkg/database/migrate/201402211147_github_enterprise_support.go @@ -8,19 +8,19 @@ func (r *Rev3) Revision() int64 { return 201402211147 } -func (r *Rev3) Up(op Operation) error { - _, err := op.AddColumn("settings", "github_domain VARCHAR(255)") +func (r *Rev3) Up(mg *MigrationDriver) error { + _, err := mg.AddColumn("settings", "github_domain VARCHAR(255)") if err != nil { return err } - _, err = op.AddColumn("settings", "github_apiurl VARCHAR(255)") + _, err = mg.AddColumn("settings", "github_apiurl VARCHAR(255)") - op.Exec("update settings set github_domain=?", "github.com") - op.Exec("update settings set github_apiurl=?", "https://api.github.com") + mg.Tx.Exec("update settings set github_domain=?", "github.com") + mg.Tx.Exec("update settings set github_apiurl=?", "https://api.github.com") return err } -func (r *Rev3) Down(op Operation) error { - _, err := op.DropColumns("settings", []string{"github_domain", "github_apiurl"}) +func (r *Rev3) Down(mg *MigrationDriver) error { + _, err := mg.DropColumns("settings", []string{"github_domain", "github_apiurl"}) return err } diff --git a/pkg/database/migrate/migrate.go b/pkg/database/migrate/migrate.go index 63cef3914..1b7cbf28c 100644 --- a/pkg/database/migrate/migrate.go +++ b/pkg/database/migrate/migrate.go @@ -64,22 +64,17 @@ type Operation interface { DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) - - Exec(query string, args ...interface{}) (sql.Result, error) - - Query(query string, args ...interface{}) (*sql.Rows, error) - - QueryRow(query string, args ...interface{}) *sql.Row } type Revision interface { - Up(op Operation) error - Down(op Operation) error + Up(mg *MigrationDriver) error + Down(mg *MigrationDriver) error Revision() int64 } type MigrationDriver struct { Tx *sql.Tx + Operation } type Migration struct { @@ -87,7 +82,9 @@ type Migration struct { revs []Revision } -var Driver func(tx *sql.Tx) Operation +type DriverFunction func(tx *sql.Tx) *MigrationDriver + +var Driver DriverFunction func New(db *sql.DB) *Migration { return &Migration{db: db} @@ -148,14 +145,14 @@ func (m *Migration) up(target, current int64) error { return err } - op := Driver(tx) + mg := Driver(tx) // loop through and execute revisions for _, rev := range m.revs { if rev.Revision() > current && rev.Revision() <= target { current = rev.Revision() // execute the revision Upgrade. - if err := rev.Up(op); err != nil { + if err := rev.Up(mg); err != nil { log.Printf("Failed to upgrade to Revision Number %v\n", current) log.Println(err) return tx.Rollback() @@ -181,7 +178,7 @@ func (m *Migration) down(target, current int64) error { return err } - op := Driver(tx) + mg := Driver(tx) // reverse the list of revisions revs := []Revision{} @@ -195,7 +192,7 @@ func (m *Migration) down(target, current int64) error { if rev.Revision() > target { current = rev.Revision() // execute the revision Upgrade. - if err := rev.Down(op); err != nil { + if err := rev.Down(mg); err != nil { log.Printf("Failed to downgrade from Revision Number %v\n", current) log.Println(err) return tx.Rollback() diff --git a/pkg/database/migrate/mysql.go b/pkg/database/migrate/mysql.go new file mode 100644 index 000000000..151c063cf --- /dev/null +++ b/pkg/database/migrate/mysql.go @@ -0,0 +1,41 @@ +package migrate + +import ( + "database/sql" + "errors" +) + +type mysqlDriver struct { + Tx *sql.Tx +} + +func MySQL(tx *sql.Tx) *MigrationDriver { + return &MigrationDriver{ + Tx: tx, + Operation: &mysqlDriver{Tx: tx}, + } +} + +func (p *mysqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *mysqlDriver) RenameTable(tableName, newName string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *mysqlDriver) DropTable(tableName string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *mysqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *mysqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} diff --git a/pkg/database/migrate/postgresql.go b/pkg/database/migrate/postgresql.go new file mode 100644 index 000000000..e3cff1ad9 --- /dev/null +++ b/pkg/database/migrate/postgresql.go @@ -0,0 +1,41 @@ +package migrate + +import ( + "database/sql" + "errors" +) + +type postgresqlDriver struct { + Tx *sql.Tx +} + +func PostgreSQL(tx *sql.Tx) *MigrationDriver { + return &MigrationDriver{ + Tx: tx, + Operation: &postgresqlDriver{Tx: tx}, + } +} + +func (p *postgresqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) RenameTable(tableName, newName string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) DropTable(tableName string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index 2cec5a026..fe6ee2013 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -9,41 +9,34 @@ import ( _ "github.com/mattn/go-sqlite3" ) -type SQLiteDriver MigrationDriver - -func SQLite(tx *sql.Tx) Operation { - return &SQLiteDriver{Tx: tx} +type sqliteDriver struct { + Tx *sql.Tx } -func (s *SQLiteDriver) Exec(query string, args ...interface{}) (sql.Result, error) { - return s.Tx.Exec(query, args...) +func SQLite(tx *sql.Tx) *MigrationDriver { + return &MigrationDriver{ + Tx: tx, + Operation: &sqliteDriver{Tx: tx}, + } } -func (s *SQLiteDriver) Query(query string, args ...interface{}) (*sql.Rows, error) { - return s.Tx.Query(query, args...) -} - -func (s *SQLiteDriver) QueryRow(query string, args ...interface{}) *sql.Row { - return s.Tx.QueryRow(query, args...) -} - -func (s *SQLiteDriver) CreateTable(tableName string, args []string) (sql.Result, error) { +func (s *sqliteDriver) CreateTable(tableName string, args []string) (sql.Result, error) { return s.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s);", tableName, strings.Join(args, ", "))) } -func (s *SQLiteDriver) RenameTable(tableName, newName string) (sql.Result, error) { +func (s *sqliteDriver) RenameTable(tableName, newName string) (sql.Result, error) { return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s;", tableName, newName)) } -func (s *SQLiteDriver) DropTable(tableName string) (sql.Result, error) { +func (s *sqliteDriver) DropTable(tableName string) (sql.Result, error) { return s.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName)) } -func (s *SQLiteDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { +func (s *sqliteDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s;", tableName, columnSpec)) } -func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { +func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { var err error var result sql.Result @@ -144,7 +137,7 @@ func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sq return result, err } -func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { +func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { var err error var result sql.Result @@ -243,7 +236,7 @@ func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string] return result, err } -func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) { +func (s *sqliteDriver) getDDLFromTable(tableName string) (string, error) { var sql string query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;` err := s.Tx.QueryRow(query, tableName).Scan(&sql) @@ -253,7 +246,7 @@ func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) { return sql, nil } -func (s *SQLiteDriver) getDDLFromIndex(tableName string) ([]string, error) { +func (s *sqliteDriver) getDDLFromIndex(tableName string) ([]string, error) { var sqls []string query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;` diff --git a/pkg/database/migrate/sqlite_test.go b/pkg/database/migrate/sqlite_test.go index c26d5b9bf..17763efdb 100644 --- a/pkg/database/migrate/sqlite_test.go +++ b/pkg/database/migrate/sqlite_test.go @@ -33,8 +33,8 @@ type AddColumnSample struct { type revision1 struct{} -func (r *revision1) Up(op Operation) error { - _, err := op.CreateTable("samples", []string{ +func (r *revision1) Up(mg *MigrationDriver) error { + _, err := mg.CreateTable("samples", []string{ "id INTEGER PRIMARY KEY AUTOINCREMENT", "imel VARCHAR(255) UNIQUE", "name VARCHAR(255)", @@ -42,8 +42,8 @@ func (r *revision1) Up(op Operation) error { return err } -func (r *revision1) Down(op Operation) error { - _, err := op.DropTable("samples") +func (r *revision1) Down(mg *MigrationDriver) error { + _, err := mg.DropTable("samples") return err } @@ -57,13 +57,13 @@ func (r *revision1) Revision() int64 { type revision2 struct{} -func (r *revision2) Up(op Operation) error { - _, err := op.RenameTable("samples", "examples") +func (r *revision2) Up(mg *MigrationDriver) error { + _, err := mg.RenameTable("samples", "examples") return err } -func (r *revision2) Down(op Operation) error { - _, err := op.RenameTable("examples", "samples") +func (r *revision2) Down(mg *MigrationDriver) error { + _, err := mg.RenameTable("examples", "samples") return err } @@ -77,16 +77,16 @@ func (r *revision2) Revision() int64 { type revision3 struct{} -func (r *revision3) Up(op Operation) error { - if _, err := op.AddColumn("samples", "url VARCHAR(255)"); err != nil { +func (r *revision3) Up(mg *MigrationDriver) error { + if _, err := mg.AddColumn("samples", "url VARCHAR(255)"); err != nil { return err } - _, err := op.AddColumn("samples", "num INTEGER") + _, err := mg.AddColumn("samples", "num INTEGER") return err } -func (r *revision3) Down(op Operation) error { - _, err := op.DropColumns("samples", []string{"num", "url"}) +func (r *revision3) Down(mg *MigrationDriver) error { + _, err := mg.DropColumns("samples", []string{"num", "url"}) return err } @@ -100,15 +100,15 @@ func (r *revision3) Revision() int64 { type revision4 struct{} -func (r *revision4) Up(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision4) Up(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "imel": "email", }) return err } -func (r *revision4) Down(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision4) Down(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "email": "imel", }) return err @@ -124,13 +124,13 @@ func (r *revision4) Revision() int64 { type revision5 struct{} -func (r *revision5) Up(op Operation) error { - _, err := op.Exec(`CREATE INDEX samples_url_name_ix ON samples (url, name)`) +func (r *revision5) Up(mg *MigrationDriver) error { + _, err := mg.Tx.Exec(`CREATE INDEX samples_url_name_ix ON samples (url, name)`) return err } -func (r *revision5) Down(op Operation) error { - _, err := op.Exec(`DROP INDEX samples_url_name_ix`) +func (r *revision5) Down(mg *MigrationDriver) error { + _, err := mg.Tx.Exec(`DROP INDEX samples_url_name_ix`) return err } @@ -143,15 +143,15 @@ func (r *revision5) Revision() int64 { // ---------- revision 6 type revision6 struct{} -func (r *revision6) Up(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision6) Up(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "url": "host", }) return err } -func (r *revision6) Down(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision6) Down(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "host": "url", }) return err @@ -166,16 +166,16 @@ func (r *revision6) Revision() int64 { // ---------- revision 7 type revision7 struct{} -func (r *revision7) Up(op Operation) error { - _, err := op.DropColumns("samples", []string{"host", "num"}) +func (r *revision7) Up(mg *MigrationDriver) error { + _, err := mg.DropColumns("samples", []string{"host", "num"}) return err } -func (r *revision7) Down(op Operation) error { - if _, err := op.AddColumn("samples", "host VARCHAR(255)"); err != nil { +func (r *revision7) Down(mg *MigrationDriver) error { + if _, err := mg.AddColumn("samples", "host VARCHAR(255)"); err != nil { return err } - _, err := op.AddColumn("samples", "num INSTEGER") + _, err := mg.AddColumn("samples", "num INSTEGER") return err } @@ -188,16 +188,16 @@ func (r *revision7) Revision() int64 { // ---------- revision 8 type revision8 struct{} -func (r *revision8) Up(op Operation) error { - if _, err := op.AddColumn("samples", "repo_id INTEGER"); err != nil { +func (r *revision8) Up(mg *MigrationDriver) error { + if _, err := mg.AddColumn("samples", "repo_id INTEGER"); err != nil { return err } - _, err := op.AddColumn("samples", "repo VARCHAR(255)") + _, err := mg.AddColumn("samples", "repo VARCHAR(255)") return err } -func (r *revision8) Down(op Operation) error { - _, err := op.DropColumns("samples", []string{"repo", "repo_id"}) +func (r *revision8) Down(mg *MigrationDriver) error { + _, err := mg.DropColumns("samples", []string{"repo", "repo_id"}) return err } @@ -210,15 +210,15 @@ func (r *revision8) Revision() int64 { // ---------- revision 9 type revision9 struct{} -func (r *revision9) Up(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision9) Up(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "repo": "repository", }) return err } -func (r *revision9) Down(op Operation) error { - _, err := op.RenameColumns("samples", map[string]string{ +func (r *revision9) Down(mg *MigrationDriver) error { + _, err := mg.RenameColumns("samples", map[string]string{ "repository": "repo", }) return err From 28fef9e3f4cbbb9dd785309e51d707a10b33aa0f Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Sat, 8 Mar 2014 18:12:43 +0700 Subject: [PATCH 02/17] Returns error if driver name is not listed. Also remove references to postgresql driver for now. And add some comments for `database.Init` method. --- Makefile | 1 - cmd/droned/drone.go | 2 +- pkg/database/database.go | 21 ++++++++++++++------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 23aaef08b..47d460c56 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,6 @@ deps: go get github.com/GeertJohan/go.rice go get github.com/GeertJohan/go.rice/rice go get github.com/go-sql-driver/mysql - go get github.com/lib/pq go get github.com/mattn/go-sqlite3 go get github.com/russross/meddler diff --git a/cmd/droned/drone.go b/cmd/droned/drone.go index 3cad9032d..ec60c54a3 100644 --- a/cmd/droned/drone.go +++ b/cmd/droned/drone.go @@ -63,7 +63,7 @@ func main() { // setup database and handlers if err := database.Init(driver, datasource); err != nil { - log.Fatal("Can't initialize database:", err) + log.Fatal("Can't initialize database: ", err) } setupStatic() setupHandlers() diff --git a/pkg/database/database.go b/pkg/database/database.go index fcaa3521c..654694e36 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -2,13 +2,13 @@ package database import ( "database/sql" + "fmt" "log" "github.com/drone/drone/pkg/database/migrate" "github.com/drone/drone/pkg/database/schema" _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" "github.com/russross/meddler" @@ -17,6 +17,13 @@ import ( // global instance of our database connection. var db *sql.DB +// Init connects to database and performs migration if necessary. +// +// Database driver name and data source information is provided by user +// from within command line, and error checking is deferred to sql.Open. +// +// Init will just bail out and returns error if driver name +// is not listed, no fallback nor default driver sets here. func Init(name, datasource string) error { driver := map[string]struct { Md *meddler.Database @@ -30,14 +37,14 @@ func Init(name, datasource string) error { meddler.MySQL, migrate.MySQL, }, - "postgresql": { - meddler.PostgreSQL, - migrate.PostgreSQL, - }, } - meddler.Default = driver[name].Md - migrate.Driver = driver[name].Mg + if drv, ok := driver[name]; ok { + meddler.Default = drv.Md + migrate.Driver = drv.Mg + } else { + return fmt.Errorf("%s driver not found", name) + } db, err := sql.Open(name, datasource) if err != nil { From 81cf496811bbb0884ef8a4a3e2767a3e077db689 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 07:08:58 +0700 Subject: [PATCH 03/17] Refactor migration. Add `ChangeColumn`, `AddIndex`, `DropIndex` method to Operation interface. --- pkg/database/database.go | 2 +- pkg/database/migrate/api.go | 36 +++++++++++++++++++++++++++++++++ pkg/database/migrate/migrate.go | 25 +---------------------- 3 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 pkg/database/migrate/api.go diff --git a/pkg/database/database.go b/pkg/database/database.go index 654694e36..4b5a0c1a1 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -27,7 +27,7 @@ var db *sql.DB func Init(name, datasource string) error { driver := map[string]struct { Md *meddler.Database - Mg migrate.DriverFunction + Mg migrate.DriverBuilder }{ "sqlite3": { meddler.SQLite, diff --git a/pkg/database/migrate/api.go b/pkg/database/migrate/api.go new file mode 100644 index 000000000..6b98ab846 --- /dev/null +++ b/pkg/database/migrate/api.go @@ -0,0 +1,36 @@ +package migrate + +import ( + "database/sql" +) + +// Operation interface covers basic migration operations. +// Implementation details is specific for each database, +// see migrate/sqlite.go for implementation reference. +type Operation interface { + CreateTable(tableName string, args []string) (sql.Result, error) + + RenameTable(tableName, newName string) (sql.Result, error) + + DropTable(tableName string) (sql.Result, error) + + AddColumn(tableName, columnSpec string) (sql.Result, error) + + ChangeColumn(tableName, columnName, newType string) (sql.Result, error) + + DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) + + RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) + + AddIndex(tableName string, columns []string, flag string) (sql.Result, error) + + DropIndex(tableName string, columns[]string) (sql.Result, error) +} + +type MigrationDriver struct { + Tx *sql.Tx + Operation +} + +type DriverBuilder func(tx *sql.Tx) *MigrationDriver + diff --git a/pkg/database/migrate/migrate.go b/pkg/database/migrate/migrate.go index 1b7cbf28c..f67e71297 100644 --- a/pkg/database/migrate/migrate.go +++ b/pkg/database/migrate/migrate.go @@ -48,35 +48,12 @@ INSERT INTO migration (revision) VALUES (?) const deleteRevisionStmt = ` DELETE FROM migration where revision = ? ` - -// Operation interface covers basic migration operations. -// Implementation details is specific for each database, -// see migrate/sqlite.go for implementation reference. -type Operation interface { - CreateTable(tableName string, args []string) (sql.Result, error) - - RenameTable(tableName, newName string) (sql.Result, error) - - DropTable(tableName string) (sql.Result, error) - - AddColumn(tableName, columnSpec string) (sql.Result, error) - - DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) - - RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) -} - type Revision interface { Up(mg *MigrationDriver) error Down(mg *MigrationDriver) error Revision() int64 } -type MigrationDriver struct { - Tx *sql.Tx - Operation -} - type Migration struct { db *sql.DB revs []Revision @@ -84,7 +61,7 @@ type Migration struct { type DriverFunction func(tx *sql.Tx) *MigrationDriver -var Driver DriverFunction +var Driver DriverBuilder func New(db *sql.DB) *Migration { return &Migration{db: db} From 489e38908af4be5f94d9884ecfe0f171c8a774ec Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 07:11:15 +0700 Subject: [PATCH 04/17] Implement ChangeColumn, AddIndex, and DropIndex methods for Operation interface. --- pkg/database/migrate/mysql.go | 24 ++++++-- pkg/database/migrate/postgresql.go | 12 ++++ pkg/database/migrate/sqlite.go | 96 +++++++++++++++++++++++++----- pkg/database/migrate/util.go | 10 ++++ 4 files changed, 121 insertions(+), 21 deletions(-) diff --git a/pkg/database/migrate/mysql.go b/pkg/database/migrate/mysql.go index 151c063cf..61421fd83 100644 --- a/pkg/database/migrate/mysql.go +++ b/pkg/database/migrate/mysql.go @@ -16,26 +16,38 @@ func MySQL(tx *sql.Tx) *MigrationDriver { } } -func (p *mysqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) { +func (m *mysqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) { return nil, errors.New("not implemented yet") } -func (p *mysqlDriver) RenameTable(tableName, newName string) (sql.Result, error) { +func (m *mysqlDriver) RenameTable(tableName, newName string) (sql.Result, error) { return nil, errors.New("not implemented yet") } -func (p *mysqlDriver) DropTable(tableName string) (sql.Result, error) { +func (m *mysqlDriver) DropTable(tableName string) (sql.Result, error) { return nil, errors.New("not implemented yet") } -func (p *mysqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { +func (m *mysqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { return nil, errors.New("not implemented yet") } -func (p *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { +func (m *mysqlDriver) ChangeColumn(tableName, columnName, newSpecs string) (sql.Result, error) { return nil, errors.New("not implemented yet") } -func (p *mysqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { +func (m *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (m *mysqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (m *mysqlDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (m *mysqlDriver) DropIndex(tableName string, columns []string) (sql.Result, error) { return nil, errors.New("not implemented yet") } diff --git a/pkg/database/migrate/postgresql.go b/pkg/database/migrate/postgresql.go index e3cff1ad9..93ba3855f 100644 --- a/pkg/database/migrate/postgresql.go +++ b/pkg/database/migrate/postgresql.go @@ -32,6 +32,10 @@ func (p *postgresqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, return nil, errors.New("not implemented yet") } +func (p *postgresqlDriver) ChangeColumn(tableName, columnName, newSpecs string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { return nil, errors.New("not implemented yet") } @@ -39,3 +43,11 @@ func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop []string) func (p *postgresqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { return nil, errors.New("not implemented yet") } + +func (p *postgresqlDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} + +func (p *postgresqlDriver) DropIndex(tableName string, columns []string) (sql.Result, error) { + return nil, errors.New("not implemented yet") +} diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index fe6ee2013..c0fdce75b 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -5,7 +5,6 @@ import ( "fmt" "strings" - "github.com/dchest/uniuri" _ "github.com/mattn/go-sqlite3" ) @@ -36,6 +35,61 @@ func (s *sqliteDriver) AddColumn(tableName, columnSpec string) (sql.Result, erro return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s;", tableName, columnSpec)) } +func (s *sqliteDriver) ChangeColumn(tableName, columnName, newType string) (sql.Result, error) { + var result sql.Result + var err error + + tableSQL, err := s.getTableDefinition(tableName) + if err != nil { + return nil, err + } + + columns, err := fetchColumns(tableSQL) + if err != nil { + return nil, err + } + + for _, column := range columns { + if columnName == strings.SplitN(column, " ", 2)[0] { + column = fmt.Sprintf("%s %s", columnName, newType) + break + } + } + + indices, err := s.getIndexDefinition(tableName) + if err != nil { + return nil, err + } + + proxy := proxyName(tableName) + if result, err = s.RenameTable(tableName, proxy); err != nil { + return nil, err + } + + if result, err = s.CreateTable(tableName, columns); err != nil { + return nil, err + } + + // Migrate data + if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s", tableName, + strings.Join(columns, ", "), proxy)); err != nil { + return result, err + } + + // Clean up proxy table + if result, err = s.DropTable(proxy); err != nil { + return result, err + } + + for _, idx := range indices { + if result, err = s.Tx.Exec(idx); err != nil { + return result, err + } + } + return result, err + +} + func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { var err error var result sql.Result @@ -44,7 +98,7 @@ func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sq return nil, fmt.Errorf("No columns to drop.") } - tableSQL, err := s.getDDLFromTable(tableName) + tableSQL, err := s.getTableDefinition(tableName) if err != nil { return nil, err } @@ -75,7 +129,7 @@ func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sq } // fetch indices for this table - oldSQLIndices, err := s.getDDLFromIndex(tableName) + oldSQLIndices, err := s.getIndexDefinition(tableName) if err != nil { return nil, err } @@ -107,8 +161,8 @@ func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sq } // Rename old table, here's our proxy - proxyName := fmt.Sprintf("%s_%s", tableName, uniuri.NewLen(16)) - if result, err := s.RenameTable(tableName, proxyName); err != nil { + proxy := proxyName(tableName) + if result, err := s.RenameTable(tableName, proxy); err != nil { return result, err } @@ -119,12 +173,12 @@ func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sq // Move data from old table if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s;", tableName, - strings.Join(selectName(preparedColumns), ", "), proxyName)); err != nil { + strings.Join(selectName(preparedColumns), ", "), proxy)); err != nil { return result, err } // Clean up proxy table - if result, err = s.DropTable(proxyName); err != nil { + if result, err = s.DropTable(proxy); err != nil { return result, err } @@ -141,7 +195,7 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] var err error var result sql.Result - tableSQL, err := s.getDDLFromTable(tableName) + tableSQL, err := s.getTableDefinition(tableName) if err != nil { return nil, err } @@ -173,7 +227,7 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] } // fetch indices for this table - oldSQLIndices, err := s.getDDLFromIndex(tableName) + oldSQLIndices, err := s.getIndexDefinition(tableName) if err != nil { return nil, err } @@ -207,8 +261,8 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] } // Rename current table - proxyName := fmt.Sprintf("%s_%s", tableName, uniuri.NewLen(16)) - if result, err := s.RenameTable(tableName, proxyName); err != nil { + proxy := proxyName(tableName) + if result, err := s.RenameTable(tableName, proxy); err != nil { return result, err } @@ -219,12 +273,12 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] // Migrate data if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s", tableName, - strings.Join(oldColumnsName, ", "), proxyName)); err != nil { + strings.Join(oldColumnsName, ", "), proxy)); err != nil { return result, err } // Clean up proxy table - if result, err = s.DropTable(proxyName); err != nil { + if result, err = s.DropTable(proxy); err != nil { return result, err } @@ -236,7 +290,19 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] return result, err } -func (s *sqliteDriver) getDDLFromTable(tableName string) (string, error) { +func (s *sqliteDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { + if strings.ToUpper(flag) != "UNIQUE" { + flag = "" + } + return s.Tx.Exec(fmt.Sprintf("CREATE %s INDEX %s ON %s (%s)", flag, indexName(tableName, columns), + tableName, strings.Join(columns, ", "))) +} + +func (s *sqliteDriver) DropIndex(tableName string, columns []string) (sql.Result, error) { + return s.Tx.Exec(fmt.Sprintf("DROP INDEX %s", indexName(tableName, columns))) +} + +func (s *sqliteDriver) getTableDefinition(tableName string) (string, error) { var sql string query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;` err := s.Tx.QueryRow(query, tableName).Scan(&sql) @@ -246,7 +312,7 @@ func (s *sqliteDriver) getDDLFromTable(tableName string) (string, error) { return sql, nil } -func (s *sqliteDriver) getDDLFromIndex(tableName string) ([]string, error) { +func (s *sqliteDriver) getIndexDefinition(tableName string) ([]string, error) { var sqls []string query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;` diff --git a/pkg/database/migrate/util.go b/pkg/database/migrate/util.go index d2001a071..dbb01e8be 100644 --- a/pkg/database/migrate/util.go +++ b/pkg/database/migrate/util.go @@ -3,6 +3,8 @@ package migrate import ( "fmt" "strings" + + "github.com/dchest/uniuri" ) func fetchColumns(sql string) ([]string, error) { @@ -30,3 +32,11 @@ func setForUpdate(left []string, right []string) string { } return strings.Join(results, ", ") } + +func proxyName(tableName string) string { + return fmt.Sprintf("%s_%s", tableName, uniuri.NewLen(16)) +} + +func indexName(tableName string, columns []string) string { + return fmt.Sprintf("idx_%s_on_%s", tableName, strings.Join(columns, "_and_")) +} From d61bb3ef6cbf781c4aed6b799bb4b3d2e2a07338 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 07:17:47 +0700 Subject: [PATCH 05/17] Fix index operation tests --- pkg/database/migrate/sqlite_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/database/migrate/sqlite_test.go b/pkg/database/migrate/sqlite_test.go index 17763efdb..bccc2e5b6 100644 --- a/pkg/database/migrate/sqlite_test.go +++ b/pkg/database/migrate/sqlite_test.go @@ -125,12 +125,12 @@ func (r *revision4) Revision() int64 { type revision5 struct{} func (r *revision5) Up(mg *MigrationDriver) error { - _, err := mg.Tx.Exec(`CREATE INDEX samples_url_name_ix ON samples (url, name)`) + _, err := mg.AddIndex("samples", []string{"url", "name"}, "") return err } func (r *revision5) Down(mg *MigrationDriver) error { - _, err := mg.Tx.Exec(`DROP INDEX samples_url_name_ix`) + _, err := mg.DropIndex("samples", []string{"url", "name"}) return err } @@ -442,9 +442,9 @@ func TestIndexOperations(t *testing.T) { t.Errorf("Can not find index: %q", err) } - indexStatement := `CREATE INDEX samples_url_name_ix ON samples (url, name)` + indexStatement := `CREATE INDEX idx_samples_on_url_and_name ON samples (url, name)` if string(esquel[1].Sql.([]byte)) != indexStatement { - t.Errorf("Can not find index") + t.Errorf("Can not find index, got: %q", esquel[1]) } // Migrate, rename indexed columns @@ -457,9 +457,9 @@ func TestIndexOperations(t *testing.T) { t.Errorf("Can not find index: %q", err) } - indexStatement = `CREATE INDEX samples_host_name_ix ON samples (host, name)` + indexStatement = `CREATE INDEX idx_samples_on_host_and_name ON samples (host, name)` if string(esquel1[1].Sql.([]byte)) != indexStatement { - t.Errorf("Can not find index, got: %s", esquel[0]) + t.Errorf("Can not find index, got: %q", esquel1[1]) } if err := mgr.Add(&revision7{}).Migrate(); err != nil { From 6a42686c290d64b5d0af8f60b9a4f266a16f6f65 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 07:49:01 +0700 Subject: [PATCH 06/17] Remove unused -path --- cmd/droned/drone.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/droned/drone.go b/cmd/droned/drone.go index ec60c54a3..46403255a 100644 --- a/cmd/droned/drone.go +++ b/cmd/droned/drone.go @@ -20,11 +20,6 @@ import ( ) var ( - // local path where the SQLite database - // should be stored. By default this is - // in the current working directory. - path string - // port the server will run on port string @@ -49,7 +44,6 @@ var ( func main() { // parse command line flags - flag.StringVar(&path, "path", "", "") flag.StringVar(&port, "port", ":8080", "") flag.StringVar(&driver, "driver", "sqlite3", "") flag.StringVar(&datasource, "datasource", "drone.sqlite", "") From 76ed21c40f824115dd64215eb8b70bc82e582179 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 11:30:39 +0700 Subject: [PATCH 07/17] Cleanup some leftover Don't have to import mattn/go-sqlite3 at pkg/database/migrate/sqlite.go Remove unused DriverFunction declaration at migrate.go --- pkg/database/migrate/api.go | 3 +-- pkg/database/migrate/migrate.go | 3 +-- pkg/database/migrate/sqlite.go | 2 -- pkg/database/migrate/sqlite_test.go | 1 + 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/database/migrate/api.go b/pkg/database/migrate/api.go index 6b98ab846..c000bfccc 100644 --- a/pkg/database/migrate/api.go +++ b/pkg/database/migrate/api.go @@ -24,7 +24,7 @@ type Operation interface { AddIndex(tableName string, columns []string, flag string) (sql.Result, error) - DropIndex(tableName string, columns[]string) (sql.Result, error) + DropIndex(tableName string, columns []string) (sql.Result, error) } type MigrationDriver struct { @@ -33,4 +33,3 @@ type MigrationDriver struct { } type DriverBuilder func(tx *sql.Tx) *MigrationDriver - diff --git a/pkg/database/migrate/migrate.go b/pkg/database/migrate/migrate.go index f67e71297..d6487a22a 100644 --- a/pkg/database/migrate/migrate.go +++ b/pkg/database/migrate/migrate.go @@ -48,6 +48,7 @@ INSERT INTO migration (revision) VALUES (?) const deleteRevisionStmt = ` DELETE FROM migration where revision = ? ` + type Revision interface { Up(mg *MigrationDriver) error Down(mg *MigrationDriver) error @@ -59,8 +60,6 @@ type Migration struct { revs []Revision } -type DriverFunction func(tx *sql.Tx) *MigrationDriver - var Driver DriverBuilder func New(db *sql.DB) *Migration { diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index c0fdce75b..dbba4d6ab 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -4,8 +4,6 @@ import ( "database/sql" "fmt" "strings" - - _ "github.com/mattn/go-sqlite3" ) type sqliteDriver struct { diff --git a/pkg/database/migrate/sqlite_test.go b/pkg/database/migrate/sqlite_test.go index bccc2e5b6..2a31754b4 100644 --- a/pkg/database/migrate/sqlite_test.go +++ b/pkg/database/migrate/sqlite_test.go @@ -6,6 +6,7 @@ import ( "strings" "testing" + _ "github.com/mattn/go-sqlite3" "github.com/russross/meddler" ) From 92dfcbfca905c2d55749d63337e0092923391652 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Mon, 10 Mar 2014 20:28:04 +0700 Subject: [PATCH 08/17] Move database schema to migration script. --- pkg/database/database.go | 9 +- pkg/database/migrate/1_setup_tables.go | 152 ++++++++++++++++++ ...140310104446_add_open_invitation_column.go | 22 +++ pkg/database/migrate/2_setup_indices.go | 83 ++++++++++ pkg/database/migrate/all.go | 3 + pkg/database/testing/testing.go | 10 +- 6 files changed, 268 insertions(+), 11 deletions(-) create mode 100644 pkg/database/migrate/1_setup_tables.go create mode 100644 pkg/database/migrate/20140310104446_add_open_invitation_column.go create mode 100644 pkg/database/migrate/2_setup_indices.go diff --git a/pkg/database/database.go b/pkg/database/database.go index 4b5a0c1a1..89eaf4782 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -25,6 +25,7 @@ var db *sql.DB // Init will just bail out and returns error if driver name // is not listed, no fallback nor default driver sets here. func Init(name, datasource string) error { + var err error driver := map[string]struct { Md *meddler.Database Mg migrate.DriverBuilder @@ -46,13 +47,11 @@ func Init(name, datasource string) error { return fmt.Errorf("%s driver not found", name) } - db, err := sql.Open(name, datasource) + db, err = sql.Open(name, datasource) if err != nil { return err } - Set(db) - migration := migrate.New(db) migration.All().Migrate() return nil @@ -70,3 +69,7 @@ func Set(database *sql.DB) { log.Fatal(err) } } + +func Close() { + db.Close() +} diff --git a/pkg/database/migrate/1_setup_tables.go b/pkg/database/migrate/1_setup_tables.go new file mode 100644 index 000000000..fa3ac2755 --- /dev/null +++ b/pkg/database/migrate/1_setup_tables.go @@ -0,0 +1,152 @@ +package migrate + +type rev1st struct{} + +var SetupTables = &rev1st{} + +func (r *rev1st) Revision() int64 { + return 1 +} + +func (r *rev1st) Up(mg *MigrationDriver) error { + if _, err := mg.CreateTable("users", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "email VARCHAR(255) UNIQUE", + "password VARCHAR(255)", + "token VARCHAR(255) UNIQUE", + "name VARCHAR(255)", + "gravatar VARCHAR(255)", + "created TIMESTAMP", + "updated TIMESTAMP", + "admin BOOLEAN", + "github_login VARCHAR(255)", + "github_token VARCHAR(255)", + "bitbucket_login VARCHAR(255)", + "bitbucket_token VARCHAR(255)", + "bitbucket_secret VARCHAR(255)", + }); err != nil { + return err + } + + if _, err := mg.CreateTable("teams", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "slug VARCHAR(255) UNIQUE", + "name VARCHAR(255)", + "email VARCHAR(255)", + "gravatar VARCHAR(255)", + "created TIMESTAMP", + "updated TIMESTAMP", + }); err != nil { + return err + } + + if _, err := mg.CreateTable("members", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "team_id INTEGER", + "user_id INTEGER", + "role INTEGER", + }); err != nil { + return err + } + + if _, err := mg.CreateTable("repos", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "slug VARCHAR(1024) UNIQUE", + "host VARCHAR(255)", + "owner VARCHAR(255)", + "name VARCHAR(255)", + "private BOOLEAN", + "disabled BOOLEAN", + "disabled_pr BOOLEAN", + "priveleged BOOLEAN", + "timeout INTEGER", + "scm VARCHAR(25)", + "url VARCHAR(1024)", + "username VARCHAR(255)", + "password VARCHAR(255)", + "public_key VARCHAR(1024)", + "private_key VARCHAR(1024)", + "params VARCHAR(2000)", + "created TIMESTAMP", + "updated TIMESTAMP", + "user_id INTEGER", + "team_id INTEGER", + }); err != nil { + return err + } + + if _, err := mg.CreateTable("commits", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "repo_id INTEGER", + "status VARCHAR(255)", + "started TIMESTAMP", + "finished TIMESTAMP", + "duration INTEGER", + "attempts INTEGER", + "hash VARCHAR(255)", + "branch VARCHAR(255)", + "pull_request VARCHAR(255)", + "author VARCHAR(255)", + "gravatar VARCHAR(255)", + "timestamp VARCHAR(255)", + "message VARCHAR(255)", + "created TIMESTAMP", + "updated TIMESTAMP", + }); err != nil { + return err + } + + if _, err := mg.CreateTable("builds", []string{ + "id INTEGER PRIMARY KEY AUTOINCREMENT", + "commit_id INTEGER", + "slug VARCHAR(255)", + "status VARCHAR(255)", + "started TIMESTAMP", + "finished TIMESTAMP", + "duration INTEGER", + "created TIMESTAMP", + "updated TIMESTAMP", + "stdout BLOB", + }); err != nil { + return err + } + + _, err := mg.CreateTable("settings", []string{ + "id INTEGER PRIMARY KEY", + "github_key VARCHAR(255)", + "github_secret VARCHAR(255)", + "bitbucket_key VARCHAR(255)", + "bitbucket_secret VARCHAR(255)", + "smtp_server VARCHAR(1024)", + "smtp_port VARCHAR(5)", + "smtp_address VARCHAR(1024)", + "smtp_username VARCHAR(1024)", + "smtp_password VARCHAR(1024)", + "hostname VARCHAR(1024)", + "scheme VARCHAR(5)", + }) + return err +} + +func (r *rev1st) Down(mg *MigrationDriver) error { + if _, err := mg.DropTable("settings"); err != nil { + return err + } + if _, err := mg.DropTable("builds"); err != nil { + return err + } + if _, err := mg.DropTable("commits"); err != nil { + return err + } + if _, err := mg.DropTable("repos"); err != nil { + return err + } + if _, err := mg.DropTable("members"); err != nil { + return err + } + if _, err := mg.DropTable("teams"); err != nil { + return err + } + _, err := mg.DropTable("users") + return err +} diff --git a/pkg/database/migrate/20140310104446_add_open_invitation_column.go b/pkg/database/migrate/20140310104446_add_open_invitation_column.go new file mode 100644 index 000000000..040dd28c9 --- /dev/null +++ b/pkg/database/migrate/20140310104446_add_open_invitation_column.go @@ -0,0 +1,22 @@ +package migrate + +type rev20140310104446 struct{} + +var AddOpenInvitationColumn = &rev20140310104446{} + +func (r *rev20140310104446) Revision() int64 { + return 20140310104446 +} + +func (r *rev20140310104446) Up(mg *MigrationDriver) error { + if _, err := mg.AddColumn("settings", "open_invitations BOOLEAN"); err != nil { + return err + } + _, err := mg.Tx.Exec("UPDATE settings SET open_invitations=0 WHERE open_invitations IS NULL") + return err +} + +func (r *rev20140310104446) Down(mg *MigrationDriver) error { + _, err := mg.DropColumns("settings", []string{"open_invitations"}) + return err +} diff --git a/pkg/database/migrate/2_setup_indices.go b/pkg/database/migrate/2_setup_indices.go new file mode 100644 index 000000000..851516aed --- /dev/null +++ b/pkg/database/migrate/2_setup_indices.go @@ -0,0 +1,83 @@ +package migrate + +type rev2nd struct{} + +var SetupIndices = &rev2nd{} + +func (r *rev2nd) Revision() int64 { + return 2 +} + +func (r *rev2nd) Up(mg *MigrationDriver) error { + if _, err := mg.AddIndex("members", []string{"team_id", "user_id"}, "unique"); err != nil { + return err + } + + if _, err := mg.AddIndex("members", []string{"team_id"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("members", []string{"user_id"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("commits", []string{"repo_id", "hash", "branch"}, "unique"); err != nil { + return err + } + + if _, err := mg.AddIndex("commits", []string{"repo_id"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("commits", []string{"repo_id", "branch"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("repos", []string{"team_id"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("repos", []string{"user_id"}, ""); err != nil { + return err + } + + if _, err := mg.AddIndex("builds", []string{"commit_id"}, ""); err != nil { + return err + } + + _, err := mg.AddIndex("builds", []string{"commit_id", "slug"}, "") + + return err +} + +func (r *rev2nd) Down(mg *MigrationDriver) error { + if _, err := mg.DropIndex("builds", []string{"commit_id", "slug"}); err != nil { + return err + } + if _, err := mg.DropIndex("builds", []string{"commit_id"}); err != nil { + return err + } + if _, err := mg.DropIndex("repos", []string{"user_id"}); err != nil { + return err + } + if _, err := mg.DropIndex("repos", []string{"team_id"}); err != nil { + return err + } + if _, err := mg.DropIndex("commits", []string{"repo_id", "branch"}); err != nil { + return err + } + if _, err := mg.DropIndex("commits", []string{"repo_id"}); err != nil { + return err + } + if _, err := mg.DropIndex("commits", []string{"repo_id", "hash", "branch"}); err != nil { + return err + } + if _, err := mg.DropIndex("members", []string{"user_id"}); err != nil { + return err + } + if _, err := mg.DropIndex("members", []string{"team_id"}); err != nil { + return err + } + _, err := mg.DropIndex("members", []string{"team_id", "user_id"}) + return err +} diff --git a/pkg/database/migrate/all.go b/pkg/database/migrate/all.go index ec5facdca..124e3ea2b 100644 --- a/pkg/database/migrate/all.go +++ b/pkg/database/migrate/all.go @@ -3,8 +3,11 @@ package migrate func (m *Migration) All() *Migration { // List all migrations here + m.Add(SetupTables) + m.Add(SetupIndices) m.Add(RenamePrivelegedToPrivileged) m.Add(GitHubEnterpriseSupport) + m.Add(AddOpenInvitationColumn) // m.Add(...) // ... diff --git a/pkg/database/testing/testing.go b/pkg/database/testing/testing.go index 69fde3787..9c8e5c79a 100644 --- a/pkg/database/testing/testing.go +++ b/pkg/database/testing/testing.go @@ -37,13 +37,7 @@ func init() { func Setup() { // create an in-memory database - db, _ = sql.Open("sqlite3", ":memory:") - - // make sure all the tables and indexes are created - database.Set(db) - - migration := migrate.New(db) - migration.All().Migrate() + database.Init("sqlite3", ":memory:") // create dummy user data user1 := User{ @@ -208,5 +202,5 @@ func Setup() { } func Teardown() { - db.Close() + database.Close() } From 9a0b29d8b1228900e9534ab6a240671cffb113e7 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Tue, 11 Mar 2014 15:10:13 +0700 Subject: [PATCH 09/17] Remove database.Set, all setup being done in database.Init now. --- pkg/database/database.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/database/database.go b/pkg/database/database.go index 89eaf4782..2c2ff216a 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -3,10 +3,8 @@ package database import ( "database/sql" "fmt" - "log" "github.com/drone/drone/pkg/database/migrate" - "github.com/drone/drone/pkg/database/schema" _ "github.com/go-sql-driver/mysql" _ "github.com/mattn/go-sqlite3" @@ -57,19 +55,6 @@ func Init(name, datasource string) error { return nil } -// Set sets the default database. -func Set(database *sql.DB) { - // set the global database - db = database - - // load the database schema. If this is - // a new database all the tables and - // indexes will be created. - if err := schema.Load(db); err != nil { - log.Fatal(err) - } -} - func Close() { db.Close() } From 89a00bd448c0ac2f744a683f35914457f20d0866 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Wed, 12 Mar 2014 07:04:34 +0700 Subject: [PATCH 10/17] Refactor tests, add test for ChangeColumn method. --- pkg/database/migrate/sqlite.go | 10 ++- pkg/database/migrate/sqlite_test.go | 111 ++++++++++++++++++---------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index dbba4d6ab..b4bd67a3c 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -47,9 +47,11 @@ func (s *sqliteDriver) ChangeColumn(tableName, columnName, newType string) (sql. return nil, err } - for _, column := range columns { - if columnName == strings.SplitN(column, " ", 2)[0] { - column = fmt.Sprintf("%s %s", columnName, newType) + columnNames := selectName(columns) + + for k, column := range columnNames { + if columnName == column { + columns[k] = fmt.Sprintf("%s %s", columnName, newType) break } } @@ -70,7 +72,7 @@ func (s *sqliteDriver) ChangeColumn(tableName, columnName, newType string) (sql. // Migrate data if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s", tableName, - strings.Join(columns, ", "), proxy)); err != nil { + strings.Join(columnNames, ", "), proxy)); err != nil { return result, err } diff --git a/pkg/database/migrate/sqlite_test.go b/pkg/database/migrate/sqlite_test.go index 2a31754b4..be9c95b6e 100644 --- a/pkg/database/migrate/sqlite_test.go +++ b/pkg/database/migrate/sqlite_test.go @@ -1,4 +1,4 @@ -package migrate +package migrate_test import ( "database/sql" @@ -6,6 +6,8 @@ import ( "strings" "testing" + . "github.com/drone/drone/pkg/database/migrate" + _ "github.com/mattn/go-sqlite3" "github.com/russross/meddler" ) @@ -231,6 +233,26 @@ func (r *revision9) Revision() int64 { // ---------- end of revision 9 +// ---------- revision 10 + +type revision10 struct{} + +func (r *revision10) Revision() int64 { + return 10 +} + +func (r *revision10) Up(mg *MigrationDriver) error { + _, err := mg.ChangeColumn("samples", "email", "varchar(512) UNIQUE") + return err +} + +func (r *revision10) Down(mg *MigrationDriver) error { + _, err := mg.ChangeColumn("samples", "email", "varchar(255) unique") + return err +} + +// ---------- end of revision 10 + var db *sql.DB var testSchema = ` @@ -253,11 +275,9 @@ func TestMigrateCreateTable(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - mgr := New(db) if err := mgr.Add(&revision1{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } sample := Sample{ @@ -266,7 +286,7 @@ func TestMigrateCreateTable(t *testing.T) { Name: "Test Tester", } if err := meddler.Save(db, "samples", &sample); err != nil { - t.Errorf("Can not save data: %q", err) + t.Fatalf("Can not save data: %q", err) } } @@ -276,22 +296,20 @@ func TestMigrateRenameTable(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - mgr := New(db) if err := mgr.Add(&revision1{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } loadFixture(t) if err := mgr.Add(&revision2{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } sample := Sample{} if err := meddler.QueryRow(db, &sample, `SELECT * FROM examples WHERE id = ?`, 2); err != nil { - t.Errorf("Can not fetch data: %q", err) + t.Fatalf("Can not fetch data: %q", err) } if sample.Imel != "foo@bar.com" { @@ -314,16 +332,14 @@ func TestMigrateAddRemoveColumns(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - mgr := New(db) if err := mgr.Add(&revision1{}, &revision3{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var columns []*TableInfo if err := meddler.QueryAll(db, &columns, `PRAGMA table_info(samples);`); err != nil { - t.Errorf("Can not access table info: %q", err) + t.Fatalf("Can not access table info: %q", err) } if len(columns) < 5 { @@ -338,16 +354,16 @@ func TestMigrateAddRemoveColumns(t *testing.T) { Num: 42, } if err := meddler.Save(db, "samples", &row); err != nil { - t.Errorf("Can not save into database: %q", err) + t.Fatalf("Can not save into database: %q", err) } if err := mgr.MigrateTo(1); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var another_columns []*TableInfo if err := meddler.QueryAll(db, &another_columns, `PRAGMA table_info(samples);`); err != nil { - t.Errorf("Can not access table info: %q", err) + t.Fatalf("Can not access table info: %q", err) } if len(another_columns) != 3 { @@ -361,22 +377,20 @@ func TestRenameColumn(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - mgr := New(db) if err := mgr.Add(&revision1{}, &revision4{}).MigrateTo(1); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } loadFixture(t) if err := mgr.MigrateTo(4); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } row := RenameSample{} if err := meddler.QueryRow(db, &row, `SELECT * FROM samples WHERE id = 3;`); err != nil { - t.Errorf("Can not query database: %q", err) + t.Fatalf("Can not query database: %q", err) } if row.Email != "crash@bandicoot.io" { @@ -390,22 +404,20 @@ func TestMigrateExistingTable(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - if _, err := db.Exec(testSchema); err != nil { - t.Errorf("Can not create database: %q", err) + t.Fatalf("Can not create database: %q", err) } loadFixture(t) mgr := New(db) if err := mgr.Add(&revision4{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var rows []*RenameSample if err := meddler.QueryAll(db, &rows, `SELECT * from samples;`); err != nil { - t.Errorf("Can not query database: %q", err) + t.Fatalf("Can not query database: %q", err) } if len(rows) != 3 { @@ -427,20 +439,18 @@ func TestIndexOperations(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - mgr := New(db) // Migrate, create index if err := mgr.Add(&revision1{}, &revision3{}, &revision5{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var esquel []*sqliteMaster // Query sqlite_master, check if index is exists. query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name='samples'` if err := meddler.QueryAll(db, &esquel, query); err != nil { - t.Errorf("Can not find index: %q", err) + t.Fatalf("Can not find index: %q", err) } indexStatement := `CREATE INDEX idx_samples_on_url_and_name ON samples (url, name)` @@ -450,12 +460,12 @@ func TestIndexOperations(t *testing.T) { // Migrate, rename indexed columns if err := mgr.Add(&revision6{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var esquel1 []*sqliteMaster if err := meddler.QueryAll(db, &esquel1, query); err != nil { - t.Errorf("Can not find index: %q", err) + t.Fatalf("Can not find index: %q", err) } indexStatement = `CREATE INDEX idx_samples_on_host_and_name ON samples (host, name)` @@ -464,12 +474,12 @@ func TestIndexOperations(t *testing.T) { } if err := mgr.Add(&revision7{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var esquel2 []*sqliteMaster if err := meddler.QueryAll(db, &esquel2, query); err != nil { - t.Errorf("Can not find index: %q", err) + t.Fatalf("Can not find index: %q", err) } if len(esquel2) != 1 { @@ -483,17 +493,15 @@ func TestColumnRedundancy(t *testing.T) { t.Fatalf("Error preparing database: %q", err) } - Driver = SQLite - migr := New(db) if err := migr.Add(&revision1{}, &revision8{}, &revision9{}).Migrate(); err != nil { - t.Errorf("Can not migrate: %q", err) + t.Fatalf("Can not migrate: %q", err) } var tableSql string query := `SELECT sql FROM sqlite_master where type='table' and name='samples'` if err := db.QueryRow(query).Scan(&tableSql); err != nil { - t.Errorf("Can not query sqlite_master: %q", err) + t.Fatalf("Can not query sqlite_master: %q", err) } if !strings.Contains(tableSql, "repository ") { @@ -501,8 +509,31 @@ func TestColumnRedundancy(t *testing.T) { } } +func TestChangeColumnType(t *testing.T) { + defer tearDown() + if err := setUp(); err != nil { + t.Fatalf("Error preparing database: %q", err) + } + + migr := New(db) + if err := migr.Add(&revision1{}, &revision4{}, &revision10{}).Migrate(); err != nil { + t.Fatalf("Can not migrate: %q", err) + } + + var tableSql string + query := `SELECT sql FROM sqlite_master where type='table' and name='samples'` + if err := db.QueryRow(query).Scan(&tableSql); err != nil { + t.Fatalf("Can not query sqlite_master: %q", err) + } + + if !strings.Contains(tableSql, "email varchar(512) UNIQUE") { + t.Errorf("Expect email type to changed: %q", tableSql) + } +} + func setUp() error { var err error + Driver = SQLite db, err = sql.Open("sqlite3", "migration_tests.sqlite") return err } @@ -515,7 +546,7 @@ func tearDown() { func loadFixture(t *testing.T) { for _, sql := range dataDump { if _, err := db.Exec(sql); err != nil { - t.Errorf("Can not insert into database: %q", err) + t.Fatalf("Can not insert into database: %q", err) } } } From 97825cf6bf883b1b697fb4d394c0fc39df67a4cb Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Wed, 12 Mar 2014 08:33:04 +0700 Subject: [PATCH 11/17] Add usage info at migration script generator. Also suppress error at the latest migration script for backward compatibility with existing database, in which open_invitations column wasn't added via migration. --- ...140310104446_add_open_invitation_column.go | 7 +++--- pkg/database/migrate/migration | 22 +++++++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pkg/database/migrate/20140310104446_add_open_invitation_column.go b/pkg/database/migrate/20140310104446_add_open_invitation_column.go index 040dd28c9..a98afa5cb 100644 --- a/pkg/database/migrate/20140310104446_add_open_invitation_column.go +++ b/pkg/database/migrate/20140310104446_add_open_invitation_column.go @@ -9,10 +9,9 @@ func (r *rev20140310104446) Revision() int64 { } func (r *rev20140310104446) Up(mg *MigrationDriver) error { - if _, err := mg.AddColumn("settings", "open_invitations BOOLEAN"); err != nil { - return err - } - _, err := mg.Tx.Exec("UPDATE settings SET open_invitations=0 WHERE open_invitations IS NULL") + // Suppress error here for backward compatibility + _, err := mg.AddColumn("settings", "open_invitations BOOLEAN") + _, err = mg.Tx.Exec("UPDATE settings SET open_invitations=0 WHERE open_invitations IS NULL") return err } diff --git a/pkg/database/migrate/migration b/pkg/database/migrate/migration index 3e3a2ff63..5b2f4c402 100755 --- a/pkg/database/migrate/migration +++ b/pkg/database/migrate/migration @@ -9,6 +9,24 @@ titleize() { echo "$1" | sed -r -e "s/-|_/ /g" -e 's/\b(.)/\U\1/g' -e 's/ //g' } +howto() { + echo "Usage:" + echo " ./migration create_sample_table" + echo "" + echo "Above invocation will create a migration script called:" + echo " ${REV}_create_sample_table.go" + echo "You can add your migration step at the Up and Down function" + echo "definition inside the file." + echo "" + echo "Database transaction available through MigrationDriver," + echo "so you can access mg.Tx (sql.Tx instance) directly," + echo "there are also some migration helpers available, see api.go" + echo "for the list of available helpers (Operation interface)." + echo "" +} + +[[ $# -eq 0 ]] && howto && exit 0 + cat > ${REV}_$filename.go << EOF package migrate @@ -20,11 +38,11 @@ func (r *rev$REV) Revision() int64 { ${TAB}return $REV } -func (r *rev$REV) Up(op Operation) error { +func (r *rev$REV) Up(mg *MigrationDriver) error { ${TAB}// Migration steps here } -func (r *rev$REV) Down(op Operation) error { +func (r *rev$REV) Down(mg *MigrationDriver) error { ${TAB}// Revert migration steps here } EOF From 5903eb8f04ba2cce99e7d7e2748f965b664cb997 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Thu, 13 Mar 2014 18:53:52 +0700 Subject: [PATCH 12/17] Add wrapper for common column types for easier column type transformation between databases. --- pkg/database/migrate/1_setup_tables.go | 169 +++++++++++++------------ pkg/database/migrate/api.go | 3 +- pkg/database/migrate/column_type.go | 75 +++++++++++ pkg/database/migrate/sqlite.go | 1 + 4 files changed, 163 insertions(+), 85 deletions(-) create mode 100644 pkg/database/migrate/column_type.go diff --git a/pkg/database/migrate/1_setup_tables.go b/pkg/database/migrate/1_setup_tables.go index fa3ac2755..e893cecc2 100644 --- a/pkg/database/migrate/1_setup_tables.go +++ b/pkg/database/migrate/1_setup_tables.go @@ -9,121 +9,122 @@ func (r *rev1st) Revision() int64 { } func (r *rev1st) Up(mg *MigrationDriver) error { + t := mg.T if _, err := mg.CreateTable("users", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "email VARCHAR(255) UNIQUE", - "password VARCHAR(255)", - "token VARCHAR(255) UNIQUE", - "name VARCHAR(255)", - "gravatar VARCHAR(255)", - "created TIMESTAMP", - "updated TIMESTAMP", - "admin BOOLEAN", - "github_login VARCHAR(255)", - "github_token VARCHAR(255)", - "bitbucket_login VARCHAR(255)", - "bitbucket_token VARCHAR(255)", - "bitbucket_secret VARCHAR(255)", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.String("email", UNIQUE), + t.String("password"), + t.String("token", UNIQUE), + t.String("name"), + t.String("gravatar"), + t.Timestamp("created"), + t.Timestamp("updated"), + t.Bool("admin"), + t.String("github_login"), + t.String("github_token"), + t.String("bitbucket_login"), + t.String("bitbucket_token"), + t.String("bitbucket_secret"), }); err != nil { return err } if _, err := mg.CreateTable("teams", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "slug VARCHAR(255) UNIQUE", - "name VARCHAR(255)", - "email VARCHAR(255)", - "gravatar VARCHAR(255)", - "created TIMESTAMP", - "updated TIMESTAMP", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.String("slug", UNIQUE), + t.String("name"), + t.String("email"), + t.String("gravatar"), + t.Timestamp("created"), + t.Timestamp("updated"), }); err != nil { return err } if _, err := mg.CreateTable("members", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "team_id INTEGER", - "user_id INTEGER", - "role INTEGER", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.Integer("team_id"), + t.Integer("user_id"), + t.Integer("role"), }); err != nil { return err } if _, err := mg.CreateTable("repos", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "slug VARCHAR(1024) UNIQUE", - "host VARCHAR(255)", - "owner VARCHAR(255)", - "name VARCHAR(255)", - "private BOOLEAN", - "disabled BOOLEAN", - "disabled_pr BOOLEAN", - "priveleged BOOLEAN", - "timeout INTEGER", - "scm VARCHAR(25)", - "url VARCHAR(1024)", - "username VARCHAR(255)", - "password VARCHAR(255)", - "public_key VARCHAR(1024)", - "private_key VARCHAR(1024)", - "params VARCHAR(2000)", - "created TIMESTAMP", - "updated TIMESTAMP", - "user_id INTEGER", - "team_id INTEGER", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.Varchar("slug", 1024, UNIQUE), + t.String("host"), + t.String("owner"), + t.String("name"), + t.Bool("private"), + t.Bool("disabled"), + t.Bool("disabled_pr"), + t.Bool("priveleged"), + t.Integer("timeout"), + t.Varchar("scm", 25), + t.Varchar("url", 1024), + t.String("username"), + t.String("password"), + t.Varchar("public_key", 1024), + t.Varchar("private_key", 1024), + t.Varchar("params", 2000), + t.Timestamp("created"), + t.Timestamp("updated"), + t.Integer("user_id"), + t.Integer("team_id"), }); err != nil { return err } if _, err := mg.CreateTable("commits", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "repo_id INTEGER", - "status VARCHAR(255)", - "started TIMESTAMP", - "finished TIMESTAMP", - "duration INTEGER", - "attempts INTEGER", - "hash VARCHAR(255)", - "branch VARCHAR(255)", - "pull_request VARCHAR(255)", - "author VARCHAR(255)", - "gravatar VARCHAR(255)", - "timestamp VARCHAR(255)", - "message VARCHAR(255)", - "created TIMESTAMP", - "updated TIMESTAMP", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.Integer("repo_id"), + t.String("status"), + t.Timestamp("started"), + t.Timestamp("finished"), + t.Integer("duration"), + t.Integer("attempts"), + t.String("hash"), + t.String("branch"), + t.String("pull_request"), + t.String("author"), + t.String("gravatar"), + t.String("timestamp"), + t.String("message"), + t.Timestamp("created"), + t.Timestamp("updated"), }); err != nil { return err } if _, err := mg.CreateTable("builds", []string{ - "id INTEGER PRIMARY KEY AUTOINCREMENT", - "commit_id INTEGER", - "slug VARCHAR(255)", - "status VARCHAR(255)", - "started TIMESTAMP", - "finished TIMESTAMP", - "duration INTEGER", - "created TIMESTAMP", - "updated TIMESTAMP", - "stdout BLOB", + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), + t.Integer("commit_id"), + t.String("slug"), + t.String("status"), + t.Timestamp("started"), + t.Timestamp("finished"), + t.Integer("duration"), + t.Timestamp("created"), + t.Timestamp("updated"), + t.Text("stdout"), }); err != nil { return err } _, err := mg.CreateTable("settings", []string{ - "id INTEGER PRIMARY KEY", - "github_key VARCHAR(255)", - "github_secret VARCHAR(255)", - "bitbucket_key VARCHAR(255)", - "bitbucket_secret VARCHAR(255)", - "smtp_server VARCHAR(1024)", - "smtp_port VARCHAR(5)", - "smtp_address VARCHAR(1024)", - "smtp_username VARCHAR(1024)", - "smtp_password VARCHAR(1024)", - "hostname VARCHAR(1024)", - "scheme VARCHAR(5)", + t.Integer("id", PRIMARYKEY), + t.String("github_key"), + t.String("github_secret"), + t.String("bitbucket_key"), + t.String("bitbucket_secret"), + t.Varchar("smtp_server", 1024), + t.Varchar("smtp_port", 5), + t.Varchar("smtp_address", 1024), + t.Varchar("smtp_username", 1024), + t.Varchar("smtp_password", 1024), + t.Varchar("hostname", 1024), + t.Varchar("scheme", 5), }) return err } diff --git a/pkg/database/migrate/api.go b/pkg/database/migrate/api.go index c000bfccc..562fedae4 100644 --- a/pkg/database/migrate/api.go +++ b/pkg/database/migrate/api.go @@ -28,8 +28,9 @@ type Operation interface { } type MigrationDriver struct { - Tx *sql.Tx Operation + T *columnType + Tx *sql.Tx } type DriverBuilder func(tx *sql.Tx) *MigrationDriver diff --git a/pkg/database/migrate/column_type.go b/pkg/database/migrate/column_type.go new file mode 100644 index 000000000..02e44deb3 --- /dev/null +++ b/pkg/database/migrate/column_type.go @@ -0,0 +1,75 @@ +package migrate + +import ( + "fmt" + "reflect" + "strings" +) + +const ( + UNIQUE int = iota + PRIMARYKEY + AUTOINCREMENT + NULL + NOTNULL + + TSTRING + TTEXT +) + +type columnType struct { + Driver string + AttrMap map[int]string +} + +var defaultMap = map[int]string{ + UNIQUE: "UNIQUE", + PRIMARYKEY: "PRIMARY KEY", + AUTOINCREMENT: "AUTOINCREMENT", + NULL: "NULL", + NOTNULL: "NOT NULL", +} + +func (c *columnType) Integer(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s INTEGER %s", colName, c.parseAttr(spec)) +} + +func (c *columnType) String(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s VARCHAR(255) %s", colName, c.parseAttr(spec)) +} + +func (c *columnType) Text(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s TEXT %s", colName, c.parseAttr(spec)) +} + +func (c *columnType) Timestamp(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s TIMESTAMP %s", colName, c.parseAttr(spec)) +} + +func (c *columnType) Bool(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s BOOLEAN %s", colName, c.parseAttr(spec)) +} + +func (c *columnType) Varchar(colName string, length int, spec ...interface{}) string { + return fmt.Sprintf("%s VARCHAR(%d) %s", colName, length, c.parseAttr(spec)) +} + +func (c *columnType) attr(flag int) string { + if v, ok := c.AttrMap[flag]; ok { + return v + } + return defaultMap[flag] +} + +func (c *columnType) parseAttr(spec []interface{}) string { + var attrs []string + for _, v := range spec { + switch reflect.ValueOf(v).Kind() { + case reflect.Int: + attrs = append(attrs, c.attr(v.(int))) + case reflect.String: + attrs = append(attrs, v.(string)) + } + } + return strings.Join(attrs, " ") +} diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index b4bd67a3c..ed2965a59 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -14,6 +14,7 @@ func SQLite(tx *sql.Tx) *MigrationDriver { return &MigrationDriver{ Tx: tx, Operation: &sqliteDriver{Tx: tx}, + T: &columnType{}, } } From 7cf4f2eb898aefc3b19d99d537e25760d04be745 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Fri, 14 Mar 2014 02:14:16 +0700 Subject: [PATCH 13/17] Preliminary MySQL support. Barely tested. Requirements: MySQL/MariaDB need to be configured with this settings: innodb_file_format = Barracuda innodb_file_per_table = On innodb_large_prefix = On to support key prefix length up to 3042 bytes. MySQL/MariaDB DSN will need this parameter: parseTime=true as per [1] The migration system itself mostly inspired by Rails (ActiveRecord), but it still rough at the edges. Could use some inputs. Next Todo: more testing! [1] https://github.com/go-sql-driver/mysql#parsetime --- pkg/database/database.go | 4 +- pkg/database/migrate/1_setup_tables.go | 4 +- pkg/database/migrate/2_setup_indices.go | 16 ++--- pkg/database/migrate/api.go | 2 +- pkg/database/migrate/migrate.go | 2 +- pkg/database/migrate/mysql.go | 78 +++++++++++++++++++++---- pkg/database/migrate/postgresql.go | 2 +- pkg/database/migrate/sqlite.go | 23 ++++---- 8 files changed, 96 insertions(+), 35 deletions(-) diff --git a/pkg/database/database.go b/pkg/database/database.go index 2c2ff216a..1966aee41 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -51,7 +51,9 @@ func Init(name, datasource string) error { } migration := migrate.New(db) - migration.All().Migrate() + if err := migration.All().Migrate(); err != nil { + return err + } return nil } diff --git a/pkg/database/migrate/1_setup_tables.go b/pkg/database/migrate/1_setup_tables.go index e893cecc2..92d62ab32 100644 --- a/pkg/database/migrate/1_setup_tables.go +++ b/pkg/database/migrate/1_setup_tables.go @@ -52,7 +52,7 @@ func (r *rev1st) Up(mg *MigrationDriver) error { if _, err := mg.CreateTable("repos", []string{ t.Integer("id", PRIMARYKEY, AUTOINCREMENT), - t.Varchar("slug", 1024, UNIQUE), + t.String("slug", UNIQUE), t.String("host"), t.String("owner"), t.String("name"), @@ -113,7 +113,7 @@ func (r *rev1st) Up(mg *MigrationDriver) error { } _, err := mg.CreateTable("settings", []string{ - t.Integer("id", PRIMARYKEY), + t.Integer("id", PRIMARYKEY, AUTOINCREMENT), t.String("github_key"), t.String("github_secret"), t.String("bitbucket_key"), diff --git a/pkg/database/migrate/2_setup_indices.go b/pkg/database/migrate/2_setup_indices.go index 851516aed..aea6db8cd 100644 --- a/pkg/database/migrate/2_setup_indices.go +++ b/pkg/database/migrate/2_setup_indices.go @@ -13,11 +13,11 @@ func (r *rev2nd) Up(mg *MigrationDriver) error { return err } - if _, err := mg.AddIndex("members", []string{"team_id"}, ""); err != nil { + if _, err := mg.AddIndex("members", []string{"team_id"}); err != nil { return err } - if _, err := mg.AddIndex("members", []string{"user_id"}, ""); err != nil { + if _, err := mg.AddIndex("members", []string{"user_id"}); err != nil { return err } @@ -25,27 +25,27 @@ func (r *rev2nd) Up(mg *MigrationDriver) error { return err } - if _, err := mg.AddIndex("commits", []string{"repo_id"}, ""); err != nil { + if _, err := mg.AddIndex("commits", []string{"repo_id"}); err != nil { return err } - if _, err := mg.AddIndex("commits", []string{"repo_id", "branch"}, ""); err != nil { + if _, err := mg.AddIndex("commits", []string{"repo_id", "branch"}); err != nil { return err } - if _, err := mg.AddIndex("repos", []string{"team_id"}, ""); err != nil { + if _, err := mg.AddIndex("repos", []string{"team_id"}); err != nil { return err } - if _, err := mg.AddIndex("repos", []string{"user_id"}, ""); err != nil { + if _, err := mg.AddIndex("repos", []string{"user_id"}); err != nil { return err } - if _, err := mg.AddIndex("builds", []string{"commit_id"}, ""); err != nil { + if _, err := mg.AddIndex("builds", []string{"commit_id"}); err != nil { return err } - _, err := mg.AddIndex("builds", []string{"commit_id", "slug"}, "") + _, err := mg.AddIndex("builds", []string{"commit_id", "slug"}) return err } diff --git a/pkg/database/migrate/api.go b/pkg/database/migrate/api.go index 562fedae4..c93d5b43f 100644 --- a/pkg/database/migrate/api.go +++ b/pkg/database/migrate/api.go @@ -22,7 +22,7 @@ type Operation interface { RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) - AddIndex(tableName string, columns []string, flag string) (sql.Result, error) + AddIndex(tableName string, columns []string, flags ...string) (sql.Result, error) DropIndex(tableName string, columns []string) (sql.Result, error) } diff --git a/pkg/database/migrate/migrate.go b/pkg/database/migrate/migrate.go index d6487a22a..ba4e8137e 100644 --- a/pkg/database/migrate/migrate.go +++ b/pkg/database/migrate/migrate.go @@ -28,7 +28,7 @@ import ( const migrationTableStmt = ` CREATE TABLE IF NOT EXISTS migration ( - revision NUMBER PRIMARY KEY + revision BIGINT PRIMARY KEY ) ` diff --git a/pkg/database/migrate/mysql.go b/pkg/database/migrate/mysql.go index 61421fd83..c05926a99 100644 --- a/pkg/database/migrate/mysql.go +++ b/pkg/database/migrate/mysql.go @@ -2,7 +2,8 @@ package migrate import ( "database/sql" - "errors" + "fmt" + "strings" ) type mysqlDriver struct { @@ -13,41 +14,96 @@ func MySQL(tx *sql.Tx) *MigrationDriver { return &MigrationDriver{ Tx: tx, Operation: &mysqlDriver{Tx: tx}, + T: &columnType{ + AttrMap: map[int]string{AUTOINCREMENT: "AUTO_INCREMENT"}, + }, } } func (m *mysqlDriver) CreateTable(tableName string, args []string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s) ROW_FORMAT=DYNAMIC", tableName, strings.Join(args, ", "))) } func (m *mysqlDriver) RenameTable(tableName, newName string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s", tableName, newName)) } func (m *mysqlDriver) DropTable(tableName string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tableName)) } func (m *mysqlDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN (%s)", tableName, columnSpec)) } func (m *mysqlDriver) ChangeColumn(tableName, columnName, newSpecs string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s MODIFY %s %s", tableName, columnName, newSpecs)) } func (m *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + for k, v := range columnsToDrop { + columnsToDrop[k] = fmt.Sprintf("DROP %s", v) + } + return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s %s", tableName, strings.Join(columnsToDrop, ", "))) } func (m *mysqlDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + var columns []string + + tableSQL, err := m.getTableDefinition(tableName) + if err != nil { + return nil, err + } + + columns, err = fetchColumns(tableSQL) + if err != nil { + return nil, err + } + + var colspec []string + for k, v := range columnChanges { + for _, col := range columns { + col = strings.Trim(col, " \n") + cols := strings.SplitN(col, " ", 2) + if quote(k) == cols[0] { + colspec = append(colspec, fmt.Sprintf("CHANGE %s %s %s", k, v, cols[1])) + break + } + } + } + + return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s %s", tableName, strings.Join(colspec, ", "))) } -func (m *mysqlDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { - return nil, errors.New("not implemented yet") +func (m *mysqlDriver) AddIndex(tableName string, columns []string, flags ...string) (sql.Result, error) { + flag := "" + if len(flags) > 0 { + switch strings.ToUpper(flags[0]) { + case "UNIQUE": + fallthrough + case "FULLTEXT": + fallthrough + case "SPATIAL": + flag = flags[0] + } + } + return m.Tx.Exec(fmt.Sprintf("CREATE %s INDEX %s ON %s (%s)", flag, + indexName(tableName, columns), tableName, strings.Join(columns, ", "))) } func (m *mysqlDriver) DropIndex(tableName string, columns []string) (sql.Result, error) { - return nil, errors.New("not implemented yet") + return m.Tx.Exec(fmt.Sprintf("DROP INDEX %s on %s", indexName(tableName, columns), tableName)) +} + +func (m *mysqlDriver) getTableDefinition(tableName string) (string, error) { + var name, def string + st := fmt.Sprintf("SHOW CREATE TABLE %s", tableName) + if err := m.Tx.QueryRow(st).Scan(&name, &def); err != nil { + return "", err + } + return def, nil +} + +func quote(name string) string { + return fmt.Sprintf("`%s`", name) } diff --git a/pkg/database/migrate/postgresql.go b/pkg/database/migrate/postgresql.go index 93ba3855f..a473412a5 100644 --- a/pkg/database/migrate/postgresql.go +++ b/pkg/database/migrate/postgresql.go @@ -44,7 +44,7 @@ func (p *postgresqlDriver) RenameColumns(tableName string, columnChanges map[str return nil, errors.New("not implemented yet") } -func (p *postgresqlDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { +func (p *postgresqlDriver) AddIndex(tableName string, columns []string, flags ...string) (sql.Result, error) { return nil, errors.New("not implemented yet") } diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index ed2965a59..bed6f7f5a 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -19,19 +19,19 @@ func SQLite(tx *sql.Tx) *MigrationDriver { } func (s *sqliteDriver) CreateTable(tableName string, args []string) (sql.Result, error) { - return s.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s);", tableName, strings.Join(args, ", "))) + return s.Tx.Exec(fmt.Sprintf("CREATE TABLE %s (%s)", tableName, strings.Join(args, ", "))) } func (s *sqliteDriver) RenameTable(tableName, newName string) (sql.Result, error) { - return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s;", tableName, newName)) + return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s", tableName, newName)) } func (s *sqliteDriver) DropTable(tableName string) (sql.Result, error) { - return s.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName)) + return s.Tx.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tableName)) } func (s *sqliteDriver) AddColumn(tableName, columnSpec string) (sql.Result, error) { - return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s;", tableName, columnSpec)) + return s.Tx.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", tableName, columnSpec)) } func (s *sqliteDriver) ChangeColumn(tableName, columnName, newType string) (sql.Result, error) { @@ -173,7 +173,7 @@ func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sq } // Move data from old table - if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s;", tableName, + if result, err = s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s", tableName, strings.Join(selectName(preparedColumns), ", "), proxy)); err != nil { return result, err } @@ -291,9 +291,12 @@ func (s *sqliteDriver) RenameColumns(tableName string, columnChanges map[string] return result, err } -func (s *sqliteDriver) AddIndex(tableName string, columns []string, flag string) (sql.Result, error) { - if strings.ToUpper(flag) != "UNIQUE" { - flag = "" +func (s *sqliteDriver) AddIndex(tableName string, columns []string, flags ...string) (sql.Result, error) { + flag := "" + if len(flags) > 0 { + if strings.ToUpper(flags[0]) == "UNIQUE" { + flag = flags[0] + } } return s.Tx.Exec(fmt.Sprintf("CREATE %s INDEX %s ON %s (%s)", flag, indexName(tableName, columns), tableName, strings.Join(columns, ", "))) @@ -305,7 +308,7 @@ func (s *sqliteDriver) DropIndex(tableName string, columns []string) (sql.Result func (s *sqliteDriver) getTableDefinition(tableName string) (string, error) { var sql string - query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?;` + query := `SELECT sql FROM sqlite_master WHERE type='table' and name=?` err := s.Tx.QueryRow(query, tableName).Scan(&sql) if err != nil { return "", err @@ -316,7 +319,7 @@ func (s *sqliteDriver) getTableDefinition(tableName string) (string, error) { func (s *sqliteDriver) getIndexDefinition(tableName string) ([]string, error) { var sqls []string - query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?;` + query := `SELECT sql FROM sqlite_master WHERE type='index' and tbl_name=?` rows, err := s.Tx.Query(query, tableName) if err != nil { return sqls, err From 2540a5fb3e27f561ccb1e0c96d08fbe61cea9150 Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Fri, 14 Mar 2014 09:24:19 +0700 Subject: [PATCH 14/17] Add docs to README on how to use mysql --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index e9d51d943..d1cd6cb0c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,27 @@ you can still get a feel for the steps: https://docs.google.com/file/d/0By8deR1ROz8memUxV0lTSGZPQUk +**Using MySQL** + +By default, Drone use sqlite as its database storage. To use MySQL/MariaDB instead, use `-driver` flag +and set it to `mysql`. You will need to set your DSN (`-datasource`) in this form: +``` + user:password@tcp(hostname:port)/dbname?parseTime=true +``` +Change it according to your database settings. The parseTime above is required since drone using +`time.Time` to represents `TIMESTAMP` data. Please refer to [1] for more options on mysql driver. + +You may also need to tweak some innodb options, especially if you're using `utf8mb4` collation type. +``` + innodb_file_format = Barracuda + innodb_file_per_table = On + innodb_large_prefix = On +``` +Please consult to the MySQL/MariaDB documentation for further information +regarding large prefix for index column and dynamic row format (which is used in Drone). + +[1] https://github.com/go-sql-driver/mysql + ### Builds Drone use a **.drone.yml** configuration file in the root of your From 571f7d02b0d224466a1c2884f254a4f2b362a2aa Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Fri, 14 Mar 2014 11:35:38 +0000 Subject: [PATCH 15/17] Fix some column type to work with both mysql and sqlite. Basically this is caused by sqlite actually doesn't have any column type restriction. also save gob type column as blob to enforce byte-string value, since varchar with utf8 collation type will take at least 3 byte to store, this will break YAML-Gob decoding at `params` column. Also fix some typo, and clean up fixtures code. --- pkg/database/commits.go | 8 +++--- pkg/database/migrate/1_setup_tables.go | 4 +-- pkg/database/migrate/column_type.go | 4 +++ pkg/database/testing/commits_test.go | 40 +++++++++++++------------- pkg/database/testing/members_test.go | 6 ++-- pkg/database/testing/testing.go | 9 ------ 6 files changed, 33 insertions(+), 38 deletions(-) diff --git a/pkg/database/commits.go b/pkg/database/commits.go index 74e0d86b4..883d3d065 100644 --- a/pkg/database/commits.go +++ b/pkg/database/commits.go @@ -16,7 +16,7 @@ SELECT id, repo_id, status, started, finished, duration, hash, branch, pull_request, author, gravatar, timestamp, message, created, updated FROM commits WHERE repo_id = ? AND branch = ? -ORDER BY created DESC +ORDER BY created DESC, id DESC LIMIT 10 ` @@ -26,7 +26,7 @@ SELECT id, repo_id, status, started, finished, duration, hash, branch, pull_request, author, gravatar, timestamp, message, created, updated FROM commits WHERE repo_id = ? AND branch = ? -ORDER BY created DESC +ORDER BY created DESC, id DESC LIMIT 1 ` @@ -57,7 +57,7 @@ WHERE r.user_id = ? AND r.team_id = 0 AND r.id = c.repo_id AND c.status IN ('Success', 'Failure') -ORDER BY c.created desc +ORDER BY c.created desc, c.id desc LIMIT 10 ` @@ -70,7 +70,7 @@ FROM repos r, commits c WHERE r.team_id = ? AND r.id = c.repo_id AND c.status IN ('Success', 'Failure') -ORDER BY c.created desc +ORDER BY c.created desc, c.id desc LIMIT 10 ` diff --git a/pkg/database/migrate/1_setup_tables.go b/pkg/database/migrate/1_setup_tables.go index 92d62ab32..79dfcdf2f 100644 --- a/pkg/database/migrate/1_setup_tables.go +++ b/pkg/database/migrate/1_setup_tables.go @@ -45,7 +45,7 @@ func (r *rev1st) Up(mg *MigrationDriver) error { t.Integer("id", PRIMARYKEY, AUTOINCREMENT), t.Integer("team_id"), t.Integer("user_id"), - t.Integer("role"), + t.String("role"), }); err != nil { return err } @@ -67,7 +67,7 @@ func (r *rev1st) Up(mg *MigrationDriver) error { t.String("password"), t.Varchar("public_key", 1024), t.Varchar("private_key", 1024), - t.Varchar("params", 2000), + t.Blob("params"), t.Timestamp("created"), t.Timestamp("updated"), t.Integer("user_id"), diff --git a/pkg/database/migrate/column_type.go b/pkg/database/migrate/column_type.go index 02e44deb3..6a6286283 100644 --- a/pkg/database/migrate/column_type.go +++ b/pkg/database/migrate/column_type.go @@ -42,6 +42,10 @@ func (c *columnType) Text(colName string, spec ...interface{}) string { return fmt.Sprintf("%s TEXT %s", colName, c.parseAttr(spec)) } +func (c *columnType) Blob(colName string, spec ...interface{}) string { + return fmt.Sprintf("%s BLOB %s", colName, c.parseAttr(spec)) +} + func (c *columnType) Timestamp(colName string, spec ...interface{}) string { return fmt.Sprintf("%s TIMESTAMP %s", colName, c.parseAttr(spec)) } diff --git a/pkg/database/testing/commits_test.go b/pkg/database/testing/commits_test.go index 1f6b8b9de..59c7f53c2 100644 --- a/pkg/database/testing/commits_test.go +++ b/pkg/database/testing/commits_test.go @@ -16,31 +16,31 @@ func TestGetCommit(t *testing.T) { } if commit.ID != 1 { - t.Errorf("Exepected ID %d, got %d", 1, commit.ID) + t.Errorf("Expected ID %d, got %d", 1, commit.ID) } if commit.Status != "Success" { - t.Errorf("Exepected Status %s, got %s", "Success", commit.Status) + t.Errorf("Expected Status %s, got %s", "Success", commit.Status) } if commit.Hash != "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608" { - t.Errorf("Exepected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) + t.Errorf("Expected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) } if commit.Branch != "master" { - t.Errorf("Exepected Branch %s, got %s", "master", commit.Branch) + t.Errorf("Expected Branch %s, got %s", "master", commit.Branch) } if commit.Author != "brad.rydzewski@gmail.com" { - t.Errorf("Exepected Author %s, got %s", "master", commit.Author) + t.Errorf("Expected Author %s, got %s", "master", commit.Author) } if commit.Message != "commit message" { - t.Errorf("Exepected Message %s, got %s", "master", commit.Message) + t.Errorf("Expected Message %s, got %s", "master", commit.Message) } if commit.Gravatar != "8c58a0be77ee441bb8f8595b7f1b4e87" { - t.Errorf("Exepected Gravatar %s, got %s", "8c58a0be77ee441bb8f8595b7f1b4e87", commit.Gravatar) + t.Errorf("Expected Gravatar %s, got %s", "8c58a0be77ee441bb8f8595b7f1b4e87", commit.Gravatar) } } @@ -54,15 +54,15 @@ func TestGetCommitHash(t *testing.T) { } if commit.ID != 1 { - t.Errorf("Exepected ID %d, got %d", 1, commit.ID) + t.Errorf("Expected ID %d, got %d", 1, commit.ID) } if commit.Hash != "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608" { - t.Errorf("Exepected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) + t.Errorf("Expected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) } if commit.Status != "Success" { - t.Errorf("Exepected Status %s, got %s", "Success", commit.Status) + t.Errorf("Expected Status %s, got %s", "Success", commit.Status) } } @@ -91,11 +91,11 @@ func TestSaveCommit(t *testing.T) { } if commit.Hash != updatedCommit.Hash { - t.Errorf("Exepected Hash %s, got %s", updatedCommit.Hash, commit.Hash) + t.Errorf("Expected Hash %s, got %s", updatedCommit.Hash, commit.Hash) } if commit.Status != "Failing" { - t.Errorf("Exepected Status %s, got %s", updatedCommit.Status, commit.Status) + t.Errorf("Expected Status %s, got %s", updatedCommit.Status, commit.Status) } } @@ -126,7 +126,7 @@ func TestListCommits(t *testing.T) { // verify commit count if len(commits) != 2 { - t.Errorf("Exepected %d commits in database, got %d", 2, len(commits)) + t.Errorf("Expected %d commits in database, got %d", 2, len(commits)) return } @@ -135,30 +135,30 @@ func TestListCommits(t *testing.T) { commit := commits[1] // TODO something strange is happening with ordering here if commit.ID != 1 { - t.Errorf("Exepected ID %d, got %d", 1, commit.ID) + t.Errorf("Expected ID %d, got %d", 1, commit.ID) } if commit.Status != "Success" { - t.Errorf("Exepected Status %s, got %s", "Success", commit.Status) + t.Errorf("Expected Status %s, got %s", "Success", commit.Status) } if commit.Hash != "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608" { - t.Errorf("Exepected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) + t.Errorf("Expected Hash %s, got %s", "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608", commit.Hash) } if commit.Branch != "master" { - t.Errorf("Exepected Branch %s, got %s", "master", commit.Branch) + t.Errorf("Expected Branch %s, got %s", "master", commit.Branch) } if commit.Author != "brad.rydzewski@gmail.com" { - t.Errorf("Exepected Author %s, got %s", "master", commit.Author) + t.Errorf("Expected Author %s, got %s", "master", commit.Author) } if commit.Message != "commit message" { - t.Errorf("Exepected Message %s, got %s", "master", commit.Message) + t.Errorf("Expected Message %s, got %s", "master", commit.Message) } if commit.Gravatar != "8c58a0be77ee441bb8f8595b7f1b4e87" { - t.Errorf("Exepected Gravatar %s, got %s", "8c58a0be77ee441bb8f8595b7f1b4e87", commit.Gravatar) + t.Errorf("Expected Gravatar %s, got %s", "8c58a0be77ee441bb8f8595b7f1b4e87", commit.Gravatar) } } diff --git a/pkg/database/testing/members_test.go b/pkg/database/testing/members_test.go index aeebcf98a..c23c1a115 100644 --- a/pkg/database/testing/members_test.go +++ b/pkg/database/testing/members_test.go @@ -64,21 +64,21 @@ func TestIsMemberAdmin(t *testing.T) { if ok, err := database.IsMemberAdmin(1, 1); err != nil { t.Error(err) } else if !ok { - t.Errorf("Expected IsMemberAdmin to return true, returned false") + t.Errorf("Expected user id 1 IsMemberAdmin to return true, returned false") } // expecting user is Admin if ok, err := database.IsMemberAdmin(2, 1); err != nil { t.Error(err) } else if !ok { - t.Errorf("Expected IsMemberAdmin to return true, returned false") + t.Errorf("Expected user id 2 IsMemberAdmin to return true, returned false") } // expecting user is NOT Admin (Write role) if ok, err := database.IsMemberAdmin(3, 1); err != nil { t.Error(err) } else if ok { - t.Errorf("Expected IsMemberAdmin to return false, returned true") + t.Errorf("Expected user id 3 IsMemberAdmin to return false, returned true") } } diff --git a/pkg/database/testing/testing.go b/pkg/database/testing/testing.go index 9c8e5c79a..57f08390b 100644 --- a/pkg/database/testing/testing.go +++ b/pkg/database/testing/testing.go @@ -2,22 +2,16 @@ package database import ( "crypto/aes" - "database/sql" "log" "github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database/encrypt" - "github.com/drone/drone/pkg/database/migrate" . "github.com/drone/drone/pkg/model" _ "github.com/mattn/go-sqlite3" "github.com/russross/meddler" ) -// in-memory database used for -// unit testing purposes. -var db *sql.DB - func init() { // create a cipher for ecnrypting and decrypting // database fields @@ -30,9 +24,6 @@ func init() { // decrypt database fields. meddler.Register("gobencrypt", &encrypt.EncryptedField{cipher}) - // notify meddler that we are working with sqlite - meddler.Default = meddler.SQLite - migrate.Driver = migrate.SQLite } func Setup() { From 44ffb708133b9d007d976c99cf996783b0d35cbb Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Sun, 16 Mar 2014 10:52:28 +0700 Subject: [PATCH 16/17] Use variadic parameters for DropColumns. Also add some comments. --- pkg/database/database.go | 1 + .../201402211147_github_enterprise_support.go | 2 +- ...140310104446_add_open_invitation_column.go | 2 +- pkg/database/migrate/all.go | 4 +++ pkg/database/migrate/api.go | 29 ++++++++++++++++++- pkg/database/migrate/column_type.go | 23 +++++++++++++-- pkg/database/migrate/migrate.go | 25 ++-------------- pkg/database/migrate/mysql.go | 2 +- pkg/database/migrate/postgresql.go | 2 +- pkg/database/migrate/sqlite.go | 2 +- pkg/database/migrate/sqlite_test.go | 8 ++--- 11 files changed, 64 insertions(+), 36 deletions(-) diff --git a/pkg/database/database.go b/pkg/database/database.go index 1966aee41..390c3ccf3 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -57,6 +57,7 @@ func Init(name, datasource string) error { return nil } +// Close database connection. func Close() { db.Close() } diff --git a/pkg/database/migrate/201402211147_github_enterprise_support.go b/pkg/database/migrate/201402211147_github_enterprise_support.go index 8c252d782..289c4bca7 100644 --- a/pkg/database/migrate/201402211147_github_enterprise_support.go +++ b/pkg/database/migrate/201402211147_github_enterprise_support.go @@ -21,6 +21,6 @@ func (r *Rev3) Up(mg *MigrationDriver) error { } func (r *Rev3) Down(mg *MigrationDriver) error { - _, err := mg.DropColumns("settings", []string{"github_domain", "github_apiurl"}) + _, err := mg.DropColumns("settings", "github_domain", "github_apiurl") return err } diff --git a/pkg/database/migrate/20140310104446_add_open_invitation_column.go b/pkg/database/migrate/20140310104446_add_open_invitation_column.go index a98afa5cb..c806f69af 100644 --- a/pkg/database/migrate/20140310104446_add_open_invitation_column.go +++ b/pkg/database/migrate/20140310104446_add_open_invitation_column.go @@ -16,6 +16,6 @@ func (r *rev20140310104446) Up(mg *MigrationDriver) error { } func (r *rev20140310104446) Down(mg *MigrationDriver) error { - _, err := mg.DropColumns("settings", []string{"open_invitations"}) + _, err := mg.DropColumns("settings", "open_invitations") return err } diff --git a/pkg/database/migrate/all.go b/pkg/database/migrate/all.go index 124e3ea2b..1748f18f8 100644 --- a/pkg/database/migrate/all.go +++ b/pkg/database/migrate/all.go @@ -1,5 +1,9 @@ package migrate +// All is called to collect all migration scripts +// and adds them to Revision list. New Revision +// should be added here ordered by its revision +// number. func (m *Migration) All() *Migration { // List all migrations here diff --git a/pkg/database/migrate/api.go b/pkg/database/migrate/api.go index c93d5b43f..6a2c4c699 100644 --- a/pkg/database/migrate/api.go +++ b/pkg/database/migrate/api.go @@ -8,29 +8,56 @@ import ( // Implementation details is specific for each database, // see migrate/sqlite.go for implementation reference. type Operation interface { + + // CreateTable may be used to create a table named `tableName` + // with its columns specification listed in `args` as an array of string CreateTable(tableName string, args []string) (sql.Result, error) + // RenameTable simply rename table from `tableName` to `newName` RenameTable(tableName, newName string) (sql.Result, error) + // DropTable drops table named `tableName` DropTable(tableName string) (sql.Result, error) + // AddColumn adds single new column to `tableName`, columnSpec is + // a standard column definition (column name included) which may looks like this: + // + // mg.AddColumn("example", "email VARCHAR(255) UNIQUE") + // + // it's equivalent to: + // + // mg.AddColumn("example", mg.T.String("email", UNIQUE)) + // AddColumn(tableName, columnSpec string) (sql.Result, error) + // ChangeColumn may be used to change the type of a column + // `newType` should always specify the column's new type even + // if the type is not meant to be change. Eg. + // + // mg.ChangeColumn("example", "name", "VARCHAR(255) UNIQUE") + // ChangeColumn(tableName, columnName, newType string) (sql.Result, error) - DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) + // DropColumns drops a list of columns + DropColumns(tableName string, columnsToDrop ...string) (sql.Result, error) + // RenameColumns will rename columns listed in `columnChanges` RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) + // AddIndex adds index on `tableName` indexed by `columns` AddIndex(tableName string, columns []string, flags ...string) (sql.Result, error) + // DropIndex drops index indexed by `columns` from `tableName` DropIndex(tableName string, columns []string) (sql.Result, error) } +// MigrationDriver drives migration script by injecting transaction object (*sql.Tx), +// `Operation` implementation and column type helper. type MigrationDriver struct { Operation T *columnType Tx *sql.Tx } +// DriverBuilder is a constructor for MigrationDriver type DriverBuilder func(tx *sql.Tx) *MigrationDriver diff --git a/pkg/database/migrate/column_type.go b/pkg/database/migrate/column_type.go index 6a6286283..2920f6f33 100644 --- a/pkg/database/migrate/column_type.go +++ b/pkg/database/migrate/column_type.go @@ -12,16 +12,20 @@ const ( AUTOINCREMENT NULL NOTNULL - - TSTRING - TTEXT ) +// columnType will be injected to migration script +// along with MigrationDriver. `AttrMap` is used to +// defines distinct column's attribute between database +// implementation. e.g. 'AUTOINCREMENT' in sqlite and +// 'AUTO_INCREMENT' in mysql. type columnType struct { Driver string AttrMap map[int]string } +// defaultMap defines default values for column's attribute +// lookup. var defaultMap = map[int]string{ UNIQUE: "UNIQUE", PRIMARYKEY: "PRIMARY KEY", @@ -30,34 +34,45 @@ var defaultMap = map[int]string{ NOTNULL: "NOT NULL", } +// Integer returns column definition for INTEGER typed column. +// Additional attributes may be specified as string or predefined key +// listed in defaultMap. func (c *columnType) Integer(colName string, spec ...interface{}) string { return fmt.Sprintf("%s INTEGER %s", colName, c.parseAttr(spec)) } +// String returns column definition for VARCHAR(255) typed column. func (c *columnType) String(colName string, spec ...interface{}) string { return fmt.Sprintf("%s VARCHAR(255) %s", colName, c.parseAttr(spec)) } +// Text returns column definition for TEXT typed column. func (c *columnType) Text(colName string, spec ...interface{}) string { return fmt.Sprintf("%s TEXT %s", colName, c.parseAttr(spec)) } +// Blob returns column definition for BLOB typed column func (c *columnType) Blob(colName string, spec ...interface{}) string { return fmt.Sprintf("%s BLOB %s", colName, c.parseAttr(spec)) } +// Timestamp returns column definition for TIMESTAMP typed column func (c *columnType) Timestamp(colName string, spec ...interface{}) string { return fmt.Sprintf("%s TIMESTAMP %s", colName, c.parseAttr(spec)) } +// Bool returns column definition for BOOLEAN typed column func (c *columnType) Bool(colName string, spec ...interface{}) string { return fmt.Sprintf("%s BOOLEAN %s", colName, c.parseAttr(spec)) } +// Varchar returns column definition for VARCHAR typed column. +// column's max length is specified as `length`. func (c *columnType) Varchar(colName string, length int, spec ...interface{}) string { return fmt.Sprintf("%s VARCHAR(%d) %s", colName, length, c.parseAttr(spec)) } +// attr returns string representation of column attribute specified as key for defaultMap. func (c *columnType) attr(flag int) string { if v, ok := c.AttrMap[flag]; ok { return v @@ -65,6 +80,8 @@ func (c *columnType) attr(flag int) string { return defaultMap[flag] } +// parseAttr reflects spec value for its type and returns the string +// representation returned by `attr` func (c *columnType) parseAttr(spec []interface{}) string { var attrs []string for _, v := range spec { diff --git a/pkg/database/migrate/migrate.go b/pkg/database/migrate/migrate.go index ba4e8137e..55c9d1d0f 100644 --- a/pkg/database/migrate/migrate.go +++ b/pkg/database/migrate/migrate.go @@ -1,24 +1,3 @@ -// Usage -// migrate.To(2) -// .Add(Version_1) -// .Add(Version_2) -// .Add(Version_3) -// .Exec(db) -// -// migrate.ToLatest() -// .Add(Version_1) -// .Add(Version_2) -// .Add(Version_3) -// .SetDialect(migrate.MySQL) -// .Exec(db) -// -// migrate.ToLatest() -// .Add(Version_1) -// .Add(Version_2) -// .Add(Version_3) -// .Backup(path) -// .Exec() - package migrate import ( @@ -72,7 +51,7 @@ func (m *Migration) Add(rev ...Revision) *Migration { return m } -// Execute the full list of migrations. +// Migrate executes the full list of migrations. func (m *Migration) Migrate() error { var target int64 if len(m.revs) > 0 { @@ -84,7 +63,7 @@ func (m *Migration) Migrate() error { return m.MigrateTo(target) } -// Execute all database migration until +// MigrateTo executes all database migration until // you are at the specified revision number. // If the revision number is less than the // current revision, then we will downgrade. diff --git a/pkg/database/migrate/mysql.go b/pkg/database/migrate/mysql.go index c05926a99..248b510c4 100644 --- a/pkg/database/migrate/mysql.go +++ b/pkg/database/migrate/mysql.go @@ -40,7 +40,7 @@ func (m *mysqlDriver) ChangeColumn(tableName, columnName, newSpecs string) (sql. return m.Tx.Exec(fmt.Sprintf("ALTER TABLE %s MODIFY %s %s", tableName, columnName, newSpecs)) } -func (m *mysqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { +func (m *mysqlDriver) DropColumns(tableName string, columnsToDrop ...string) (sql.Result, error) { for k, v := range columnsToDrop { columnsToDrop[k] = fmt.Sprintf("DROP %s", v) } diff --git a/pkg/database/migrate/postgresql.go b/pkg/database/migrate/postgresql.go index a473412a5..6225daad7 100644 --- a/pkg/database/migrate/postgresql.go +++ b/pkg/database/migrate/postgresql.go @@ -36,7 +36,7 @@ func (p *postgresqlDriver) ChangeColumn(tableName, columnName, newSpecs string) return nil, errors.New("not implemented yet") } -func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { +func (p *postgresqlDriver) DropColumns(tableName string, columnsToDrop ...string) (sql.Result, error) { return nil, errors.New("not implemented yet") } diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index bed6f7f5a..3b14189dc 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -91,7 +91,7 @@ func (s *sqliteDriver) ChangeColumn(tableName, columnName, newType string) (sql. } -func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop []string) (sql.Result, error) { +func (s *sqliteDriver) DropColumns(tableName string, columnsToDrop ...string) (sql.Result, error) { var err error var result sql.Result diff --git a/pkg/database/migrate/sqlite_test.go b/pkg/database/migrate/sqlite_test.go index be9c95b6e..7e4987f76 100644 --- a/pkg/database/migrate/sqlite_test.go +++ b/pkg/database/migrate/sqlite_test.go @@ -89,7 +89,7 @@ func (r *revision3) Up(mg *MigrationDriver) error { } func (r *revision3) Down(mg *MigrationDriver) error { - _, err := mg.DropColumns("samples", []string{"num", "url"}) + _, err := mg.DropColumns("samples", "num", "url") return err } @@ -128,7 +128,7 @@ func (r *revision4) Revision() int64 { type revision5 struct{} func (r *revision5) Up(mg *MigrationDriver) error { - _, err := mg.AddIndex("samples", []string{"url", "name"}, "") + _, err := mg.AddIndex("samples", []string{"url", "name"}) return err } @@ -170,7 +170,7 @@ func (r *revision6) Revision() int64 { type revision7 struct{} func (r *revision7) Up(mg *MigrationDriver) error { - _, err := mg.DropColumns("samples", []string{"host", "num"}) + _, err := mg.DropColumns("samples", "host", "num") return err } @@ -200,7 +200,7 @@ func (r *revision8) Up(mg *MigrationDriver) error { } func (r *revision8) Down(mg *MigrationDriver) error { - _, err := mg.DropColumns("samples", []string{"repo", "repo_id"}) + _, err := mg.DropColumns("samples", "repo", "repo_id") return err } From 7eb06fc0d5a4827a28b0631280e92084adc2ac2f Mon Sep 17 00:00:00 2001 From: Nurahmadie Date: Sun, 16 Mar 2014 11:28:55 +0700 Subject: [PATCH 17/17] Use NullString instead of checking for error. --- pkg/database/migrate/sqlite.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pkg/database/migrate/sqlite.go b/pkg/database/migrate/sqlite.go index 3b14189dc..99264b92a 100644 --- a/pkg/database/migrate/sqlite.go +++ b/pkg/database/migrate/sqlite.go @@ -326,16 +326,13 @@ func (s *sqliteDriver) getIndexDefinition(tableName string) ([]string, error) { } for rows.Next() { - var sql string + var sql sql.NullString if err := rows.Scan(&sql); err != nil { - // This error came from autoindex, since its sql value is null, - // we want to continue. - if strings.Contains(err.Error(), "Scan pair: -> *string") { - continue - } return sqls, err } - sqls = append(sqls, sql) + if sql.Valid { + sqls = append(sqls, sql.String) + } } if err := rows.Err(); err != nil {