mirror of
https://github.com/status-im/migrate.git
synced 2025-02-24 00:38:07 +00:00
commit
fb8698d89a
1
database/ql/migration/33_create_table.down.sql
Normal file
1
database/ql/migration/33_create_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS pets;
|
3
database/ql/migration/33_create_table.up.sql
Normal file
3
database/ql/migration/33_create_table.up.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CREATE TABLE pets (
|
||||||
|
name string
|
||||||
|
);
|
1
database/ql/migration/44_alter_table.down.sql
Normal file
1
database/ql/migration/44_alter_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS pets;
|
1
database/ql/migration/44_alter_table.up.sql
Normal file
1
database/ql/migration/44_alter_table.up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE pets ADD predator bool;;
|
212
database/ql/ql.go
Normal file
212
database/ql/ql.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package ql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
nurl "net/url"
|
||||||
|
|
||||||
|
_ "github.com/cznic/ql/driver"
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
"github.com/mattes/migrate/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
database.Register("ql", &Ql{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultMigrationsTable = "schema_migrations"
|
||||||
|
var (
|
||||||
|
ErrDatabaseDirty = fmt.Errorf("database is dirty")
|
||||||
|
ErrNilConfig = fmt.Errorf("no config")
|
||||||
|
ErrNoDatabaseName = fmt.Errorf("no database name")
|
||||||
|
ErrAppendPEM = fmt.Errorf("failed to append PEM")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
MigrationsTable string
|
||||||
|
DatabaseName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ql 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 := &Ql{
|
||||||
|
db: instance,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
if err := mx.ensureVersionTable(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mx, nil
|
||||||
|
}
|
||||||
|
func (m *Ql) ensureVersionTable() error {
|
||||||
|
tx, err := m.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (version uint64,dirty bool);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS version_unique ON %s (version);
|
||||||
|
`, m.config.MigrationsTable, m.config.MigrationsTable)); err != nil {
|
||||||
|
if err := tx.Rollback(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Ql) Open(url string) (database.Driver, error) {
|
||||||
|
purl, err := nurl.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbfile := strings.Replace(migrate.FilterCustomQuery(purl).String(), "ql://", "", 1)
|
||||||
|
db, err := sql.Open("ql", 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 *Ql) Close() error {
|
||||||
|
return m.db.Close()
|
||||||
|
}
|
||||||
|
func (m *Ql) Drop() error {
|
||||||
|
query := `SELECT Name FROM __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 {
|
||||||
|
if strings.HasPrefix(tableName, "__") == false {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Ql) Lock() error {
|
||||||
|
if m.isLocked {
|
||||||
|
return database.ErrLocked
|
||||||
|
}
|
||||||
|
m.isLocked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Ql) Unlock() error {
|
||||||
|
if !m.isLocked {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.isLocked = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Ql) Run(migration io.Reader) error {
|
||||||
|
migr, err := ioutil.ReadAll(migration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query := string(migr[:])
|
||||||
|
|
||||||
|
return m.executeQuery(query)
|
||||||
|
}
|
||||||
|
func (m *Ql) 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 *Ql) SetVersion(version int, dirty bool) error {
|
||||||
|
tx, err := m.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Err: "transaction start failed"}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := "TRUNCATE TABLE " + 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 *Ql) 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
|
||||||
|
}
|
62
database/ql/ql_test.go
Normal file
62
database/ql/ql_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package ql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "github.com/cznic/ql/driver"
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
dt "github.com/mattes/migrate/database/testing"
|
||||||
|
_ "github.com/mattes/migrate/source/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "ql-driver-test")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
fmt.Printf("DB path : %s\n", filepath.Join(dir, "ql.db"))
|
||||||
|
p := &Ql{}
|
||||||
|
addr := fmt.Sprintf("ql://%s", filepath.Join(dir, "ql.db"))
|
||||||
|
d, err := p.Open(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("ql", filepath.Join(dir, "ql.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