mirror of
https://github.com/status-im/migrate.git
synced 2025-02-23 16:28:08 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
2aa16f5f66
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
|||||||
SOURCE ?= file go-bindata github aws-s3 google-cloud-storage
|
SOURCE ?= file go-bindata github aws-s3 google-cloud-storage
|
||||||
DATABASE ?= postgres mysql redshift cassandra
|
DATABASE ?= postgres mysql redshift cassandra sqlite3
|
||||||
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
|
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
|
||||||
TEST_FLAGS ?=
|
TEST_FLAGS ?=
|
||||||
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
|
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
|
||||||
|
@ -25,7 +25,7 @@ Database drivers run migrations. [Add a new database?](database/driver.go)
|
|||||||
* [Redshift](database/redshift)
|
* [Redshift](database/redshift)
|
||||||
* [Ql](database/ql)
|
* [Ql](database/ql)
|
||||||
* [Cassandra](database/cassandra)
|
* [Cassandra](database/cassandra)
|
||||||
* [SQLite](database/sqlite) ([todo #165](https://github.com/mattes/migrate/issues/165))
|
* [SQLite](database/sqlite3)
|
||||||
* [MySQL/ MariaDB](database/mysql)
|
* [MySQL/ MariaDB](database/mysql)
|
||||||
* [Neo4j](database/neo4j) ([todo #167](https://github.com/mattes/migrate/issues/167))
|
* [Neo4j](database/neo4j) ([todo #167](https://github.com/mattes/migrate/issues/167))
|
||||||
* [MongoDB](database/mongodb) ([todo #169](https://github.com/mattes/migrate/issues/169))
|
* [MongoDB](database/mongodb) ([todo #169](https://github.com/mattes/migrate/issues/169))
|
||||||
|
7
cli/build_sqlite3.go
Normal file
7
cli/build_sqlite3.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build sqlite3
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/mattes/migrate/database/sqlite3"
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
# mysql
|
# MySQL
|
||||||
|
|
||||||
`mysql://user:password@tcp(host:port)/dbname?query`
|
`mysql://user:password@tcp(host:port)/dbname?query`
|
||||||
|
|
||||||
@ -15,6 +15,35 @@
|
|||||||
| `x-tls-key` | | Key file location. |
|
| `x-tls-key` | | Key file location. |
|
||||||
| `x-tls-insecure-skip-verify` | | Whether or not to use SSL (true\|false) |
|
| `x-tls-insecure-skip-verify` | | Whether or not to use SSL (true\|false) |
|
||||||
|
|
||||||
|
## Use with existing client
|
||||||
|
|
||||||
|
If you use the MySQL driver with existing database client, you must create the client with parameter `multiStatements=true`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
"github.com/mattes/migrate/database/mysql"
|
||||||
|
_ "github.com/mattes/migrate/source/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db, _ := sql.Open("mysql", "user:password@tcp(host:port)/dbname?multiStatements=true")
|
||||||
|
driver, _ := mysql.WithInstance(db, &mysql.Config{})
|
||||||
|
m, _ := migrate.NewWithDatabaseInstance(
|
||||||
|
"file:///migrations",
|
||||||
|
"mysql",
|
||||||
|
driver,
|
||||||
|
)
|
||||||
|
|
||||||
|
m.Steps(2)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Upgrading from v1
|
## Upgrading from v1
|
||||||
|
|
||||||
1. Write down the current migration version from schema_migrations
|
1. Write down the current migration version from schema_migrations
|
||||||
|
1
database/sqlite3/migration/33_create_table.down.sql
Normal file
1
database/sqlite3/migration/33_create_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS pets;
|
3
database/sqlite3/migration/33_create_table.up.sql
Normal file
3
database/sqlite3/migration/33_create_table.up.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CREATE TABLE pets (
|
||||||
|
name string
|
||||||
|
);
|
1
database/sqlite3/migration/44_alter_table.down.sql
Normal file
1
database/sqlite3/migration/44_alter_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS pets;
|
1
database/sqlite3/migration/44_alter_table.up.sql
Normal file
1
database/sqlite3/migration/44_alter_table.up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE pets ADD predator bool;
|
214
database/sqlite3/sqlite3.go
Normal file
214
database/sqlite3/sqlite3.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
"github.com/mattes/migrate/database"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
nurl "net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
database.Register("sqlite3", &Sqlite{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultMigrationsTable = "schema_migrations"
|
||||||
|
var (
|
||||||
|
ErrDatabaseDirty = fmt.Errorf("database is dirty")
|
||||||
|
ErrNilConfig = fmt.Errorf("no config")
|
||||||
|
ErrNoDatabaseName = fmt.Errorf("no database name")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
MigrationsTable string
|
||||||
|
DatabaseName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sqlite struct {
|
||||||
|
db *sql.DB
|
||||||
|
isLocked bool
|
||||||
|
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, ErrNilConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := instance.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(config.MigrationsTable) == 0 {
|
||||||
|
config.MigrationsTable = DefaultMigrationsTable
|
||||||
|
}
|
||||||
|
|
||||||
|
mx := &Sqlite{
|
||||||
|
db: instance,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
if err := mx.ensureVersionTable(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) ensureVersionTable() error {
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (version uint64,dirty bool);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS version_unique ON %s (version);
|
||||||
|
`, DefaultMigrationsTable, DefaultMigrationsTable)
|
||||||
|
|
||||||
|
if _, err := m.db.Exec(query); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Open(url string) (database.Driver, error) {
|
||||||
|
purl, err := nurl.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbfile := strings.Replace(migrate.FilterCustomQuery(purl).String(), "sqlite3://", "", 1)
|
||||||
|
db, err := sql.Open("sqlite3", dbfile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
migrationsTable := purl.Query().Get("x-migrations-table")
|
||||||
|
if len(migrationsTable) == 0 {
|
||||||
|
migrationsTable = DefaultMigrationsTable
|
||||||
|
}
|
||||||
|
mx, err := WithInstance(db, &Config{
|
||||||
|
DatabaseName: purl.Path,
|
||||||
|
MigrationsTable: migrationsTable,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Close() error {
|
||||||
|
return m.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Drop() error {
|
||||||
|
query := `SELECT name FROM sqlite_master WHERE type = 'table';`
|
||||||
|
tables, err := m.db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
defer tables.Close()
|
||||||
|
tableNames := make([]string, 0)
|
||||||
|
for tables.Next() {
|
||||||
|
var tableName string
|
||||||
|
if err := tables.Scan(&tableName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tableName) > 0 {
|
||||||
|
tableNames = append(tableNames, tableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(tableNames) > 0 {
|
||||||
|
for _, t := range tableNames {
|
||||||
|
query := "DROP TABLE " + t
|
||||||
|
err = m.executeQuery(query)
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := m.ensureVersionTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query := "VACUUM"
|
||||||
|
_, err = m.db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Lock() error {
|
||||||
|
if m.isLocked {
|
||||||
|
return database.ErrLocked
|
||||||
|
}
|
||||||
|
m.isLocked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Unlock() error {
|
||||||
|
if !m.isLocked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.isLocked = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Run(migration io.Reader) error {
|
||||||
|
migr, err := ioutil.ReadAll(migration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query := string(migr[:])
|
||||||
|
|
||||||
|
return m.executeQuery(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) executeQuery(query string) error {
|
||||||
|
tx, err := m.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Err: "transaction start failed"}
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(query); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) SetVersion(version int, dirty bool) error {
|
||||||
|
tx, err := m.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Err: "transaction start failed"}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := "DELETE FROM " + m.config.MigrationsTable
|
||||||
|
if _, err := tx.Exec(query); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if version >= 0 {
|
||||||
|
query := fmt.Sprintf(`INSERT INTO %s (version, dirty) VALUES (%d, '%t')`, m.config.MigrationsTable, version, dirty)
|
||||||
|
if _, err := tx.Exec(query); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Sqlite) Version() (version int, dirty bool, err error) {
|
||||||
|
query := "SELECT version, dirty FROM " + m.config.MigrationsTable + " LIMIT 1"
|
||||||
|
err = m.db.QueryRow(query).Scan(&version, &dirty)
|
||||||
|
if err != nil {
|
||||||
|
return database.NilVersion, false, nil
|
||||||
|
}
|
||||||
|
return version, dirty, nil
|
||||||
|
}
|
61
database/sqlite3/sqlite3_test.go
Normal file
61
database/sqlite3/sqlite3_test.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
dt "github.com/mattes/migrate/database/testing"
|
||||||
|
_ "github.com/mattes/migrate/source/file"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "sqlite3-driver-test")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
fmt.Printf("DB path : %s\n", filepath.Join(dir, "sqlite3.db"))
|
||||||
|
p := &Sqlite{}
|
||||||
|
addr := fmt.Sprintf("sqlite3://%s", filepath.Join(dir, "sqlite3.db"))
|
||||||
|
d, err := p.Open(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", filepath.Join(dir, "sqlite3.db"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := db.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
dt.Test(t, d, []byte("CREATE TABLE t (Qty int, Name string);"))
|
||||||
|
driver, err := WithInstance(db, &Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
if err := d.Drop(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := migrate.NewWithDatabaseInstance(
|
||||||
|
"file://./migration",
|
||||||
|
"ql", driver)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("UP")
|
||||||
|
err = m.Up()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user