Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfe3ec0fce | ||
|
|
01b25ee18b | ||
|
|
afc973c4e5 | ||
|
|
250543a561 | ||
|
|
1dffba9fa5 | ||
|
|
964e6bc070 | ||
|
|
1887c0f3e3 | ||
|
|
62dba4f8ea | ||
|
|
78ca4eb007 | ||
|
|
d21443f74b | ||
|
|
7f4000c0d6 | ||
|
|
ecd74e1ce6 | ||
|
|
9f14676773 | ||
|
|
7dde434714 | ||
|
|
fff063351d | ||
|
|
5498e70562 | ||
|
|
0da98940d6 | ||
|
|
4cfc044489 | ||
|
|
83c703f09f | ||
|
|
b7845a779e | ||
|
|
09640b136e | ||
|
|
7ca71e567d | ||
|
|
c7653285b2 | ||
|
|
f4979054e2 | ||
|
|
305a63a447 | ||
|
|
528bc5bbbb | ||
|
|
83e27c8b88 | ||
|
|
e8bfb1b2b3 | ||
|
|
2cd9bfcd04 | ||
|
|
21ccec2aa1 | ||
|
|
fe6711645a | ||
|
|
adf1cff659 | ||
|
|
da12e27062 | ||
|
|
d2ad6f8ecf | ||
|
|
bbb58aca8d | ||
|
|
5aed294d25 |
+7
-5
@@ -2,18 +2,20 @@ language: go
|
||||
sudo: required
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- curl -L https://github.com/docker/compose/releases/download/1.4.2/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
- curl -L https://github.com/docker/compose/releases/download/1.11.2/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
- chmod +x docker-compose
|
||||
- sudo mv docker-compose /usr/local/bin
|
||||
- sed -i -e 's/golang/golang:'"$TRAVIS_GO_VERSION"'/' docker-compose.yml
|
||||
|
||||
|
||||
script: make test
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
# You are looking at a deprecated version.
|
||||
# Use the [latest version](https://github.com/mattes/migrate) instead.
|
||||
|
||||
---
|
||||
|
||||
# migrate
|
||||
|
||||
[](https://travis-ci.org/mattes/migrate)
|
||||
[](https://godoc.org/github.com/mattes/migrate)
|
||||
[](https://travis-ci.org/mattes/migrate)
|
||||
|
||||
A migration helper written in Go. Use it in your existing Golang code
|
||||
or run commands via the CLI.
|
||||
|
||||
```
|
||||
GoCode import github.com/mattes/migrate/migrate
|
||||
CLI go get -u github.com/mattes/migrate
|
||||
GoCode import gopkg.in/mattes/migrate.v1/migrate
|
||||
CLI go get -u gopkg.in/mattes/migrate.v1
|
||||
```
|
||||
|
||||
__Features__
|
||||
|
||||
* Super easy to implement [Driver interface](http://godoc.org/github.com/mattes/migrate/driver#Driver).
|
||||
* Super easy to implement [Driver interface](http://godoc.org/gopkg.in/mattes/migrate.v1/driver#Driver).
|
||||
* Gracefully quit running migrations on ``^C``.
|
||||
* No magic search paths routines, no hard-coded config files.
|
||||
* CLI is build on top of the ``migrate package``.
|
||||
@@ -29,15 +33,16 @@ __Features__
|
||||
* [Ql](driver/ql)
|
||||
* [MongoDB](driver/mongodb)
|
||||
* [CrateDB](driver/crate)
|
||||
* [MSSQL](driver/mssql)
|
||||
|
||||
Need another driver? Just implement the [Driver interface](http://godoc.org/github.com/mattes/migrate/driver#Driver) and open a PR.
|
||||
Need another driver? Just implement the [Driver interface](http://godoc.org/gopkg.in/mattes/migrate.v1/driver#Driver) and open a PR.
|
||||
|
||||
|
||||
## Usage from Terminal
|
||||
|
||||
```bash
|
||||
# install
|
||||
go get github.com/mattes/migrate
|
||||
go get gopkg.in/mattes/migrate.v1
|
||||
|
||||
# create new migration file in path
|
||||
migrate -url driver://url -path ./migrations create migration_file_xyz
|
||||
@@ -76,13 +81,13 @@ migrate -url driver://url -path ./migrations goto v
|
||||
|
||||
## Usage in Go
|
||||
|
||||
See GoDoc here: http://godoc.org/github.com/mattes/migrate/migrate
|
||||
See GoDoc here: http://godoc.org/gopkg.in/mattes/migrate.v1/migrate
|
||||
|
||||
```go
|
||||
import "github.com/mattes/migrate/migrate"
|
||||
import "gopkg.in/mattes/migrate.v1/migrate"
|
||||
|
||||
// Import any required drivers so that they are registered and available
|
||||
import _ "github.com/mattes/migrate/driver/mysql"
|
||||
import _ "gopkg.in/mattes/migrate.v1/driver/mysql"
|
||||
|
||||
// use synchronous versions of migration functions ...
|
||||
allErrors, ok := migrate.UpSync("driver://url", "./path")
|
||||
|
||||
+8
-2
@@ -1,12 +1,13 @@
|
||||
go: &go
|
||||
image: golang
|
||||
working_dir: /go/src/github.com/mattes/migrate
|
||||
working_dir: /go/src/gopkg.in/mattes/migrate.v1
|
||||
volumes:
|
||||
- $GOPATH:/go
|
||||
- $GOPATH/src/github.com/mattes/migrate:/go/src/gopkg.in/mattes/migrate.v1
|
||||
go-test:
|
||||
<<: *go
|
||||
command: sh -c 'go get -t -v ./... && go test -v ./...'
|
||||
links:
|
||||
- mssql
|
||||
- postgres
|
||||
- mysql
|
||||
- cassandra
|
||||
@@ -30,3 +31,8 @@ crate:
|
||||
image: crate
|
||||
mongo:
|
||||
image: mongo:3.2.6
|
||||
mssql:
|
||||
image: microsoft/mssql-server-linux
|
||||
environment:
|
||||
SA_PASSWORD: Passw0rd
|
||||
ACCEPT_EULA: "Y"
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
migrate -url bash://xxx -path ./migrations create increment_xyz
|
||||
migrate -url bash://xxx -path ./migrations up
|
||||
migrate help # for more info
|
||||
```
|
||||
```
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
package bash
|
||||
|
||||
import (
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
|
||||
@@ -9,49 +9,21 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
// Driver implements migrate Driver interface
|
||||
type Driver struct {
|
||||
session *gocql.Session
|
||||
}
|
||||
|
||||
const (
|
||||
tableName = "schema_migrations"
|
||||
versionRow = 1
|
||||
tableName = "schema_migrations"
|
||||
)
|
||||
|
||||
type counterStmt bool
|
||||
|
||||
func (c counterStmt) Exec(session *gocql.Session) error {
|
||||
var version int64
|
||||
if err := session.Query("SELECT version FROM "+tableName+" WHERE versionRow = ?", versionRow).Scan(&version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bool(c) {
|
||||
version++
|
||||
} else {
|
||||
version--
|
||||
}
|
||||
|
||||
return session.Query("UPDATE "+tableName+" SET version = ? WHERE versionRow = ?", version, versionRow).Exec()
|
||||
}
|
||||
|
||||
const (
|
||||
up counterStmt = true
|
||||
down counterStmt = false
|
||||
)
|
||||
|
||||
// Cassandra Driver URL format:
|
||||
// cassandra://host:port/keyspace?protocol=version&consistency=level
|
||||
//
|
||||
// Examples:
|
||||
// cassandra://localhost/SpaceOfKeys?protocol=4
|
||||
// cassandra://localhost/SpaceOfKeys?protocol=4&consistency=all
|
||||
// cassandra://localhost/SpaceOfKeys?consistency=quorum
|
||||
// Initialize will be called first
|
||||
func (driver *Driver) Initialize(rawurl string) error {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
@@ -68,7 +40,8 @@ func (driver *Driver) Initialize(rawurl string) error {
|
||||
cluster.Timeout = 1 * time.Minute
|
||||
|
||||
if len(u.Query().Get("consistency")) > 0 {
|
||||
consistency, err := parseConsistency(u.Query().Get("consistency"))
|
||||
var consistency gocql.Consistency
|
||||
consistency, err = parseConsistency(u.Query().Get("consistency"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,7 +50,8 @@ func (driver *Driver) Initialize(rawurl string) error {
|
||||
}
|
||||
|
||||
if len(u.Query().Get("protocol")) > 0 {
|
||||
protoversion, err := strconv.Atoi(u.Query().Get("protocol"))
|
||||
var protoversion int
|
||||
protoversion, err = strconv.Atoi(u.Query().Get("protocol"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,7 +64,7 @@ func (driver *Driver) Initialize(rawurl string) error {
|
||||
password, passwordSet := u.User.Password()
|
||||
|
||||
if passwordSet == false {
|
||||
return fmt.Errorf("Missing password. Please provide password.")
|
||||
return fmt.Errorf("Missing password. Please provide password")
|
||||
}
|
||||
|
||||
cluster.Authenticator = gocql.PasswordAuthenticator{
|
||||
@@ -112,53 +86,63 @@ func (driver *Driver) Initialize(rawurl string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close last function to be called. Closes cassandra session
|
||||
func (driver *Driver) Close() error {
|
||||
driver.session.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) ensureVersionTableExists() error {
|
||||
err := driver.session.Query("CREATE TABLE IF NOT EXISTS " + tableName + " (version int, versionRow bigint primary key);").Exec()
|
||||
err := driver.session.Query(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id uuid primary key, version bigint)", tableName)).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = driver.Version()
|
||||
if err != nil {
|
||||
if err.Error() == "not found" {
|
||||
return driver.session.Query("UPDATE "+tableName+" SET version = ? WHERE versionRow = ?", 1, versionRow).Exec()
|
||||
}
|
||||
if _, err = driver.Version(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilenameExtension return extension of migrations files
|
||||
func (driver *Driver) FilenameExtension() string {
|
||||
return "cql"
|
||||
}
|
||||
|
||||
func (driver *Driver) version(d direction.Direction, invert bool) error {
|
||||
var stmt counterStmt
|
||||
switch d {
|
||||
case direction.Up:
|
||||
stmt = up
|
||||
case direction.Down:
|
||||
stmt = down
|
||||
func (driver *Driver) updateVersion(version uint64, dir direction.Direction) error {
|
||||
var ids []string
|
||||
var id string
|
||||
var err error
|
||||
iter := driver.session.Query(fmt.Sprintf("SELECT id FROM %s WHERE version >= ? ALLOW FILTERING", tableName), version).Iter()
|
||||
for iter.Scan(&id) {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
if invert {
|
||||
stmt = !stmt
|
||||
if len(ids) > 0 {
|
||||
err = driver.session.Query(fmt.Sprintf("DELETE FROM %s WHERE id IN ?", tableName), ids).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return stmt.Exec(driver.session)
|
||||
if dir == direction.Up {
|
||||
return driver.session.Query(fmt.Sprintf("INSERT INTO %s (id, version) VALUES (uuid(), ?)", tableName), version).Exec()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Migrate run migration file. Restore previous version in case of fail
|
||||
func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
var err error
|
||||
previousVersion, err := driver.Version()
|
||||
if err != nil {
|
||||
close(pipe)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Invert version direction if we couldn't apply the changes for some reason.
|
||||
if err := driver.version(f.Direction, true); err != nil {
|
||||
pipe <- err
|
||||
if updErr := driver.updateVersion(previousVersion, direction.Up); updErr != nil {
|
||||
pipe <- updErr
|
||||
}
|
||||
pipe <- err
|
||||
}
|
||||
@@ -166,7 +150,7 @@ func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
}()
|
||||
|
||||
pipe <- f
|
||||
if err = driver.version(f.Direction, false); err != nil {
|
||||
if err = driver.updateVersion(f.Version, f.Direction); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,10 +170,11 @@ func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Version return current version
|
||||
func (driver *Driver) Version() (uint64, error) {
|
||||
var version int64
|
||||
err := driver.session.Query("SELECT version FROM "+tableName+" WHERE versionRow = ?", versionRow).Scan(&version)
|
||||
return uint64(version) - 1, err
|
||||
err := driver.session.Query(fmt.Sprintf("SELECT max(version) FROM %s", tableName)).Scan(&version)
|
||||
return uint64(version), err
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
func TestMigrate(t *testing.T) {
|
||||
@@ -21,10 +21,10 @@ func TestMigrate(t *testing.T) {
|
||||
|
||||
host := os.Getenv("CASSANDRA_PORT_9042_TCP_ADDR")
|
||||
port := os.Getenv("CASSANDRA_PORT_9042_TCP_PORT")
|
||||
driverUrl := "cassandra://" + host + ":" + port + "/system"
|
||||
driverURL := "cassandra://" + host + ":" + port + "/system"
|
||||
|
||||
// prepare a clean test database
|
||||
u, err := url.Parse(driverUrl)
|
||||
u, err := url.Parse(driverURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -35,23 +35,25 @@ func TestMigrate(t *testing.T) {
|
||||
cluster.Timeout = 1 * time.Minute
|
||||
|
||||
session, err = cluster.CreateSession()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := session.Query(`DROP KEYSPACE IF EXISTS migrate;`).Exec(); err != nil {
|
||||
if err = session.Query(`DROP KEYSPACE IF EXISTS migrate;`).Exec(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := session.Query(`CREATE KEYSPACE IF NOT EXISTS migrate WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1};`).Exec(); err != nil {
|
||||
if err = session.Query(`CREATE KEYSPACE IF NOT EXISTS migrate WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1};`).Exec(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cluster.Keyspace = "migrate"
|
||||
session, err = cluster.CreateSession()
|
||||
driverUrl = "cassandra://" + host + ":" + port + "/migrate"
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
driverURL = "cassandra://" + host + ":" + port + "/migrate"
|
||||
|
||||
d := &Driver{}
|
||||
if err := d.Initialize(driverUrl); err != nil {
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,4 @@ limitation of Crate. So handle situations with failed migrations with care!
|
||||
migrate -url http://host:port -path ./db/migrations create add_field_to_table
|
||||
migrate -url http://host:port -path ./db/migrations up
|
||||
migrate help # for more info
|
||||
```
|
||||
```
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"strings"
|
||||
|
||||
_ "github.com/herenow/go-crate"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -97,8 +97,8 @@ func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
func splitContent(content string) []string {
|
||||
lines := strings.Split(content, ";")
|
||||
resultLines := make([]string, 0, len(lines))
|
||||
for i, line := range lines {
|
||||
line = strings.Replace(lines[i], ";", "", -1)
|
||||
for i := range lines {
|
||||
line := strings.Replace(lines[i], ";", "", -1)
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
resultLines = append(resultLines, line)
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
func TestContentSplit(t *testing.T) {
|
||||
|
||||
+18
-2
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
neturl "net/url" // alias to allow `url string` func signature in New
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
)
|
||||
|
||||
// Driver is the interface type that needs to implemented by all drivers.
|
||||
@@ -43,7 +43,7 @@ func New(url string) (Driver, error) {
|
||||
|
||||
d := GetDriver(u.Scheme)
|
||||
if d == nil {
|
||||
return nil, fmt.Errorf("Driver '%s' not found.", u.Scheme)
|
||||
return nil, fmt.Errorf("Driver '%s' not found", u.Scheme)
|
||||
}
|
||||
verifyFilenameExtension(u.Scheme, d)
|
||||
if err := d.Initialize(url); err != nil {
|
||||
@@ -53,6 +53,22 @@ func New(url string) (Driver, error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// FilenameExtensionFromURL return extension for migration files. Used for create migrations
|
||||
func FilenameExtensionFromURL(url string) (string, error) {
|
||||
u, err := neturl.Parse(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
d := GetDriver(u.Scheme)
|
||||
if d == nil {
|
||||
return "", fmt.Errorf("Driver '%s' not found", u.Scheme)
|
||||
}
|
||||
verifyFilenameExtension(u.Scheme, d)
|
||||
|
||||
return d.FilenameExtension(), nil
|
||||
}
|
||||
|
||||
// verifyFilenameExtension panics if the driver's filename extension
|
||||
// is not correct or empty.
|
||||
func verifyFilenameExtension(driverName string, d Driver) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## Usage in Go
|
||||
|
||||
```go
|
||||
import "github.com/mattes/migrate/migrate"
|
||||
import "gopkg.in/mattes/migrate.v1/migrate"
|
||||
|
||||
// Import your migration methods package so that they are registered and available for the MongoDB driver.
|
||||
// There is no need to import the MongoDB driver explicitly, as it should already be imported by your migration methods package.
|
||||
@@ -59,14 +59,14 @@ V001_some_migration_operation_down
|
||||
|
||||
## Methods registration
|
||||
|
||||
For a detailed example see: [sample_mongodb_migrator.go](https://github.com/mattes/migrate/blob/master/driver/mongodb/example/sample_mongdb_migrator.go)
|
||||
For a detailed example see: [sample_mongodb_migrator.go](https://github.com/mattes/migrate/blob/v1/driver/mongodb/example/sample_mongdb_migrator.go)
|
||||
|
||||
```go
|
||||
package my_mongo_db_migrator
|
||||
|
||||
import (
|
||||
"github.com/mattes/migrate/driver/mongodb"
|
||||
"github.com/mattes/migrate/driver/mongodb/gomethods"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb/gomethods"
|
||||
"gopkg.in/mgo.v2"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,16 +3,17 @@ package example
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/driver/mongodb"
|
||||
"github.com/mattes/migrate/driver/mongodb/gomethods"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb/gomethods"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
type ExpectedMigrationResult struct {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"github.com/mattes/migrate/driver/mongodb/gomethods"
|
||||
_ "github.com/mattes/migrate/driver/mongodb/gomethods"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
|
||||
"github.com/mattes/migrate/driver/mongodb"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb/gomethods"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/mongodb/gomethods"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb"
|
||||
)
|
||||
|
||||
type SampleMongoDbMigrator struct {
|
||||
|
||||
@@ -3,11 +3,12 @@ package gomethods
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
)
|
||||
|
||||
type MethodNotFoundError string
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
type FakeGoMethodsInvoker struct {
|
||||
|
||||
@@ -2,8 +2,9 @@ package gomethods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
)
|
||||
|
||||
var methodsReceiversMu sync.Mutex
|
||||
|
||||
@@ -2,14 +2,15 @@ package mongodb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/driver/mongodb/gomethods"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/driver/mongodb/gomethods"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
type UnregisteredMethodsReceiverError string
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# MSSQL Driver
|
||||
|
||||
* Runs migrations in transactions.
|
||||
That means that if a migration fails, it will be safely rolled back.
|
||||
* Tries to return helpful error messages.
|
||||
* Stores migration version details in table ``schema_migrations``.
|
||||
This table will be auto-generated.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
migrate -url="sqlserver://sa:Passw0rd@localhost:1433?database=master" -path ./db/migrations create add_field_to_table
|
||||
migrate -url="sqlserver://sa:Passw0rd@localhost:1433?database=master" -path ./db/migrations up
|
||||
migrate help # for more info
|
||||
|
||||
# see more docs: https://github.com/denisenkom/go-mssqldb
|
||||
```
|
||||
|
||||
## Authors
|
||||
|
||||
* Andras Laczi, https://github.com/alaczi
|
||||
@@ -0,0 +1,128 @@
|
||||
// Package mssql implements the Driver interface.
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
const tableName = "schema_migrations"
|
||||
|
||||
func (driver *Driver) Initialize(url string) error {
|
||||
db, err := sql.Open("mssql", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
driver.db = db
|
||||
|
||||
if err := driver.ensureVersionTableExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) Close() error {
|
||||
if err := driver.db.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) ensureVersionTableExists() error {
|
||||
if _, err := driver.db.Exec("if object_id('" + tableName + "') is null create table [" + tableName + "] ([version] BIGINT NOT NULL PRIMARY KEY);"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) FilenameExtension() string {
|
||||
return "sql"
|
||||
}
|
||||
|
||||
func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
defer close(pipe)
|
||||
pipe <- f
|
||||
|
||||
tx, err := driver.db.Begin()
|
||||
if err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
|
||||
if f.Direction == direction.Up {
|
||||
if _, err := tx.Exec("INSERT INTO "+tableName+" (version) VALUES (?)", f.Version); err != nil {
|
||||
pipe <- err
|
||||
if err := tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
} else if f.Direction == direction.Down {
|
||||
if _, err := tx.Exec("DELETE FROM "+tableName+" WHERE version=?", f.Version); err != nil {
|
||||
pipe <- err
|
||||
if err := tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := f.ReadContent(); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(string(f.Content)); err != nil {
|
||||
switch msErr := err.(type) {
|
||||
case *mssql.Error:
|
||||
errorPart := file.LinesBeforeAndAfter(f.Content, int(msErr.LineNo), 5, 5, true)
|
||||
pipe <- errors.New(fmt.Sprintf("%v: %s in line %v:\n\n%s", msErr.Number, msErr.Message, msErr.LineNo, string(errorPart)))
|
||||
if err := tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
default:
|
||||
pipe <- err
|
||||
if err := tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (driver *Driver) Version() (uint64, error) {
|
||||
var version uint64
|
||||
err := driver.db.QueryRow("SELECT version FROM " + tableName + " ORDER BY version DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY").Scan(&version)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return 0, nil
|
||||
case err != nil:
|
||||
return 0, err
|
||||
default:
|
||||
return version, nil
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.RegisterDriver("mssql", &Driver{})
|
||||
driver.RegisterDriver("sqlserver", &Driver{})
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// TestMigrate runs some additional tests on Migrate().
|
||||
// Basic testing is already done in migrate/migrate_test.go
|
||||
func TestMigrate(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
|
||||
host := os.Getenv("MSSQL_PORT_1433_TCP_ADDR")
|
||||
port := os.Getenv("MSSQL_PORT_1433_TCP_PORT")
|
||||
driverURL := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%s;database=%s;encrypt=disable;log=2;TrustServerCertificate=true",
|
||||
host,
|
||||
"sa",
|
||||
"Passw0rd",
|
||||
port,
|
||||
"master",
|
||||
)
|
||||
// retry connection for 2 minutes
|
||||
until := time.Now().Add(time.Second * 120)
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
var connection *sql.DB
|
||||
var err error
|
||||
// prepare clean database
|
||||
for tick := range ticker.C {
|
||||
if tick.After(until) {
|
||||
ticker.Stop()
|
||||
break
|
||||
}
|
||||
connection, err = sql.Open("mssql", driverURL)
|
||||
err = connection.Ping()
|
||||
if err == nil {
|
||||
ticker.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Failed to connect to mssql docker container after 2 minutes", err)
|
||||
}
|
||||
|
||||
if _, err := connection.Exec(`
|
||||
DROP TABLE IF EXISTS yolo;
|
||||
DROP TABLE IF EXISTS ` + tableName + `;`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &Driver{}
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// testing idempotency: second call should be a no-op, since table already exists
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
files := []file.File{
|
||||
{
|
||||
Path: "/foobar",
|
||||
FileName: "001_foobar.up.sql",
|
||||
Version: 1,
|
||||
Name: "foobar",
|
||||
Direction: direction.Up,
|
||||
Content: []byte(`
|
||||
CREATE TABLE yolo (
|
||||
id BIGINT IDENTITY NOT NULL PRIMARY KEY
|
||||
);
|
||||
`),
|
||||
},
|
||||
{
|
||||
Path: "/foobar",
|
||||
FileName: "002_foobar.down.sql",
|
||||
Version: 1,
|
||||
Name: "foobar",
|
||||
Direction: direction.Down,
|
||||
Content: []byte(`
|
||||
DROP TABLE yolo;
|
||||
`),
|
||||
},
|
||||
{
|
||||
Path: "/foobar",
|
||||
FileName: "002_foobar.up.sql",
|
||||
Version: 2,
|
||||
Name: "foobar",
|
||||
Direction: direction.Up,
|
||||
Content: []byte(`
|
||||
CREATE TABLE error (
|
||||
id THIS WILL CAUSE AN ERROR
|
||||
)
|
||||
`),
|
||||
},
|
||||
{
|
||||
Path: "/foobar",
|
||||
FileName: "20170118205923_demo.up.sql",
|
||||
Version: 20170118205923,
|
||||
Name: "demo",
|
||||
Direction: direction.Up,
|
||||
Content: []byte(`
|
||||
CREATE TABLE demo (
|
||||
id BIGINT IDENTITY NOT NULL PRIMARY KEY
|
||||
)
|
||||
`),
|
||||
},
|
||||
{
|
||||
Path: "/foobar",
|
||||
FileName: "20170118205923_demo.down.sql",
|
||||
Version: 20170118205923,
|
||||
Name: "demo",
|
||||
Direction: direction.Down,
|
||||
Content: []byte(`
|
||||
DROP TABLE demo
|
||||
`),
|
||||
},
|
||||
}
|
||||
|
||||
pipe := pipep.New()
|
||||
go d.Migrate(files[0], pipe)
|
||||
errs := pipep.ReadErrors(pipe)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
pipe = pipep.New()
|
||||
go d.Migrate(files[1], pipe)
|
||||
errs = pipep.ReadErrors(pipe)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
pipe = pipep.New()
|
||||
go d.Migrate(files[2], pipe)
|
||||
errs = pipep.ReadErrors(pipe)
|
||||
if len(errs) == 0 {
|
||||
t.Error("Expected test case to fail")
|
||||
}
|
||||
|
||||
pipe = pipep.New()
|
||||
go d.Migrate(files[3], pipe)
|
||||
errs = pipep.ReadErrors(pipe)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
pipe = pipep.New()
|
||||
go d.Migrate(files[4], pipe)
|
||||
errs = pipep.ReadErrors(pipe)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
if err := d.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -21,4 +21,4 @@ See full [DSN (Data Source Name) documentation](https://github.com/go-sql-driver
|
||||
|
||||
## Authors
|
||||
|
||||
* Matthias Kadenbach, https://github.com/mattes
|
||||
* Matthias Kadenbach, https://github.com/mattes
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// TestMigrate runs some additional tests on Migrate().
|
||||
|
||||
@@ -17,4 +17,4 @@ migrate help # for more info
|
||||
```
|
||||
## Author
|
||||
|
||||
* Carlos Forero, https://github.com/carlosforero
|
||||
* Carlos Forero, https://github.com/carlosforero
|
||||
|
||||
+114
-104
@@ -2,156 +2,166 @@
|
||||
package neo4j
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"strings"
|
||||
"errors"
|
||||
"github.com/jmcvetta/neoism"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jmcvetta/neoism"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
db *neoism.Database
|
||||
db *neoism.Database
|
||||
}
|
||||
|
||||
const labelName = "SchemaMigration"
|
||||
|
||||
func (driver *Driver) Initialize(url string) error {
|
||||
url = strings.Replace(url,"neo4j","http",1)
|
||||
url = strings.Replace(url, "neo4j", "http", 1)
|
||||
|
||||
db, err := neoism.Connect(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db, err := neoism.Connect(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
driver.db = db
|
||||
driver.db = db
|
||||
|
||||
if err := driver.ensureVersionConstraintExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if err := driver.ensureVersionConstraintExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) Close() error {
|
||||
driver.db = nil
|
||||
return nil
|
||||
driver.db = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) FilenameExtension() string {
|
||||
return "cql"
|
||||
return "cql"
|
||||
}
|
||||
|
||||
func (driver *Driver) ensureVersionConstraintExists() error {
|
||||
uc, _ := driver.db.UniqueConstraints("SchemaMigration", "version")
|
||||
if len(uc) == 0 {
|
||||
_, err := driver.db.CreateUniqueConstraint("SchemaMigration", "version")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
uc, _ := driver.db.UniqueConstraints("SchemaMigration", "version")
|
||||
if len(uc) == 0 {
|
||||
_, err := driver.db.CreateUniqueConstraint("SchemaMigration", "version")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *Driver) setVersion(d direction.Direction, v uint64, invert bool) error {
|
||||
|
||||
cqUp := neoism.CypherQuery {
|
||||
Statement: `CREATE (n:SchemaMigration {version: {Version}}) RETURN n`,
|
||||
Parameters: neoism.Props{"Version": v},
|
||||
}
|
||||
cqUp := neoism.CypherQuery{
|
||||
Statement: `CREATE (n:SchemaMigration {version: {Version}}) RETURN n`,
|
||||
Parameters: neoism.Props{"Version": v},
|
||||
}
|
||||
|
||||
cqDown := neoism.CypherQuery {
|
||||
Statement: `MATCH (n:SchemaMigration {version: {Version}}) DELETE n`,
|
||||
Parameters: neoism.Props{"Version": v},
|
||||
}
|
||||
cqDown := neoism.CypherQuery{
|
||||
Statement: `MATCH (n:SchemaMigration {version: {Version}}) DELETE n`,
|
||||
Parameters: neoism.Props{"Version": v},
|
||||
}
|
||||
|
||||
var cq neoism.CypherQuery
|
||||
switch d {
|
||||
case direction.Up:
|
||||
if invert { cq = cqDown } else { cq = cqUp }
|
||||
case direction.Down:
|
||||
if invert { cq = cqUp } else { cq = cqDown }
|
||||
}
|
||||
return driver.db.Cypher(&cq)
|
||||
var cq neoism.CypherQuery
|
||||
switch d {
|
||||
case direction.Up:
|
||||
if invert {
|
||||
cq = cqDown
|
||||
} else {
|
||||
cq = cqUp
|
||||
}
|
||||
case direction.Down:
|
||||
if invert {
|
||||
cq = cqUp
|
||||
} else {
|
||||
cq = cqDown
|
||||
}
|
||||
}
|
||||
return driver.db.Cypher(&cq)
|
||||
}
|
||||
|
||||
func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
var err error
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Invert version direction if we couldn't apply the changes for some reason.
|
||||
if err := driver.setVersion(f.Direction, f.Version, true); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
pipe <- err
|
||||
}
|
||||
close(pipe)
|
||||
}()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Invert version direction if we couldn't apply the changes for some reason.
|
||||
if err := driver.setVersion(f.Direction, f.Version, true); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
pipe <- err
|
||||
}
|
||||
close(pipe)
|
||||
}()
|
||||
|
||||
pipe <- f
|
||||
pipe <- f
|
||||
|
||||
if err = driver.setVersion(f.Direction, f.Version, false); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err = driver.setVersion(f.Direction, f.Version, false); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
if err = f.ReadContent(); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err = f.ReadContent(); err != nil {
|
||||
pipe <- err
|
||||
return
|
||||
}
|
||||
cQueries := []*neoism.CypherQuery{}
|
||||
|
||||
cQueries := []*neoism.CypherQuery{}
|
||||
// Neoism doesn't support multiple statements per query.
|
||||
cqlStmts := bytes.Split(f.Content, []byte(";"))
|
||||
|
||||
// Neoism doesn't support multiple statements per query.
|
||||
cqlStmts := bytes.Split(f.Content, []byte(";"))
|
||||
for _, cqlStmt := range cqlStmts {
|
||||
cqlStmt = bytes.TrimSpace(cqlStmt)
|
||||
if len(cqlStmt) > 0 {
|
||||
cq := neoism.CypherQuery{Statement: string(cqlStmt)}
|
||||
cQueries = append(cQueries, &cq)
|
||||
}
|
||||
}
|
||||
|
||||
for _, cqlStmt := range cqlStmts {
|
||||
cqlStmt = bytes.TrimSpace(cqlStmt)
|
||||
if len(cqlStmt) > 0 {
|
||||
cq := neoism.CypherQuery{Statement: string(cqlStmt)}
|
||||
cQueries = append( cQueries, &cq )
|
||||
}
|
||||
}
|
||||
var tx *neoism.Tx
|
||||
|
||||
var tx *neoism.Tx
|
||||
tx, err = driver.db.Begin(cQueries)
|
||||
if err != nil {
|
||||
pipe <- err
|
||||
for _, err := range tx.Errors {
|
||||
pipe <- errors.New(fmt.Sprintf("%v", err.Message))
|
||||
}
|
||||
if err = tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
tx, err = driver.db.Begin(cQueries)
|
||||
if err != nil {
|
||||
pipe <- err
|
||||
for _, err := range tx.Errors {
|
||||
pipe <- errors.New(fmt.Sprintf("%v", err.Message))
|
||||
}
|
||||
if err = tx.Rollback(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
pipe <- err
|
||||
for _, err := range tx.Errors {
|
||||
pipe <- errors.New(fmt.Sprintf("%v", err.Message))
|
||||
}
|
||||
return
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
pipe <- err
|
||||
for _, err := range tx.Errors {
|
||||
pipe <- errors.New(fmt.Sprintf("%v", err.Message))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (driver *Driver) Version() (uint64, error) {
|
||||
res := []struct {Version uint64 `json:"n.version"`}{}
|
||||
res := []struct {
|
||||
Version uint64 `json:"n.version"`
|
||||
}{}
|
||||
|
||||
cq := neoism.CypherQuery{
|
||||
Statement: `MATCH (n:SchemaMigration)
|
||||
cq := neoism.CypherQuery{
|
||||
Statement: `MATCH (n:SchemaMigration)
|
||||
RETURN n.version ORDER BY n.version DESC LIMIT 1`,
|
||||
Result: &res,
|
||||
}
|
||||
Result: &res,
|
||||
}
|
||||
|
||||
if err := driver.db.Cypher(&cq); err != nil || len(res) == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return res[0].Version, nil
|
||||
if err := driver.db.Cypher(&cq); err != nil || len(res) == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return res[0].Version, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.RegisterDriver("neo4j", &Driver{})
|
||||
driver.RegisterDriver("neo4j", &Driver{})
|
||||
driver.RegisterDriver("neo4js", &Driver{})
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmcvetta/neoism"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// TestMigrate runs some additional tests on Migrate().
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
@@ -43,7 +43,7 @@ func (driver *Driver) Close() error {
|
||||
}
|
||||
|
||||
func (driver *Driver) ensureVersionTableExists() error {
|
||||
r := driver.db.QueryRow("SELECT count(*) FROM information_schema.tables WHERE table_name = $1;", tableName)
|
||||
r := driver.db.QueryRow("SELECT count(*) FROM information_schema.tables WHERE table_name = $1 AND table_schema = (SELECT current_schema());", tableName)
|
||||
c := 0
|
||||
if err := r.Scan(&c); err != nil {
|
||||
return err
|
||||
@@ -139,5 +139,7 @@ func (driver *Driver) Version() (uint64, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.RegisterDriver("postgres", &Driver{})
|
||||
drv := Driver{}
|
||||
driver.RegisterDriver("postgres", &drv)
|
||||
driver.RegisterDriver("postgresql", &drv)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// TestMigrate runs some additional tests on Migrate().
|
||||
@@ -19,10 +19,10 @@ func TestMigrate(t *testing.T) {
|
||||
|
||||
host := os.Getenv("POSTGRES_PORT_5432_TCP_ADDR")
|
||||
port := os.Getenv("POSTGRES_PORT_5432_TCP_PORT")
|
||||
driverUrl := "postgres://postgres@" + host + ":" + port + "/template1?sslmode=disable"
|
||||
driverURL := "postgres://postgres@" + host + ":" + port + "/template1?sslmode=disable"
|
||||
|
||||
// prepare clean database
|
||||
connection, err := sql.Open("postgres", driverUrl)
|
||||
connection, err := sql.Open("postgres", driverURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -33,12 +33,12 @@ func TestMigrate(t *testing.T) {
|
||||
}
|
||||
|
||||
d := &Driver{}
|
||||
if err := d.Initialize(driverUrl); err != nil {
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// testing idempotency: second call should be a no-op, since table already exists
|
||||
if err := d.Initialize(driverUrl); err != nil {
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -3,13 +3,14 @@ package ql
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
_ "github.com/cznic/ql/driver"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const tableName = "schema_migration"
|
||||
@@ -127,4 +128,4 @@ func (d *Driver) ensureVersionTableExists() error {
|
||||
func init() {
|
||||
driver.RegisterDriver("ql+file", &Driver{})
|
||||
driver.RegisterDriver("ql+memory", &Driver{})
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import (
|
||||
var driversMu sync.Mutex
|
||||
var drivers = make(map[string]Driver)
|
||||
|
||||
// Registers a driver so it can be created from its name. Drivers should
|
||||
// RegisterDriver register a driver so it can be created from its name. Drivers should
|
||||
// call this from an init() function so that they registers themselves on
|
||||
// import
|
||||
func RegisterDriver(name string, driver Driver) {
|
||||
@@ -23,7 +23,7 @@ func RegisterDriver(name string, driver Driver) {
|
||||
drivers[name] = driver
|
||||
}
|
||||
|
||||
// Retrieves a registered driver by name
|
||||
// GetDriver retrieves a registered driver by name
|
||||
func GetDriver(name string) Driver {
|
||||
driversMu.Lock()
|
||||
defer driversMu.Unlock()
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
@@ -96,9 +96,9 @@ func (driver *Driver) Migrate(f file.File, pipe chan interface{}) {
|
||||
|
||||
if isErr {
|
||||
// The sqlite3 library only provides error codes, not position information. Output what we do know
|
||||
pipe <- errors.New(fmt.Sprintf("SQLite Error (%s); Extended (%s)\nError: %s", sqliteErr.Code.Error(), sqliteErr.ExtendedCode.Error(), sqliteErr.Error()))
|
||||
pipe <- fmt.Errorf("SQLite Error (%s); Extended (%s)\nError: %s", sqliteErr.Code.Error(), sqliteErr.ExtendedCode.Error(), sqliteErr.Error())
|
||||
} else {
|
||||
pipe <- errors.New(fmt.Sprintf("An error occurred: %s", err.Error()))
|
||||
pipe <- fmt.Errorf("An error occurred: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := tx.Rollback(); err != nil {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// TestMigrate runs some additional tests on Migrate()
|
||||
@@ -17,7 +17,7 @@ func TestMigrate(t *testing.T) {
|
||||
}
|
||||
|
||||
driverFile := ":memory:"
|
||||
driverUrl := "sqlite3://" + driverFile
|
||||
driverURL := "sqlite3://" + driverFile
|
||||
|
||||
// prepare clean database
|
||||
connection, err := sql.Open("sqlite3", driverFile)
|
||||
@@ -31,7 +31,7 @@ func TestMigrate(t *testing.T) {
|
||||
}
|
||||
|
||||
d := &Driver{}
|
||||
if err := d.Initialize(driverUrl); err != nil {
|
||||
if err := d.Initialize(driverURL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -5,7 +5,6 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
@@ -13,6 +12,8 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
var filenameRegex = `^([0-9]+)_(.*)\.(up|down)\.%s$`
|
||||
@@ -137,10 +138,10 @@ func (mf *MigrationFiles) From(version uint64, relativeN int) (Files, error) {
|
||||
|
||||
if d == direction.Up && migrationFile.Version > version && migrationFile.UpFile != nil {
|
||||
files = append(files, *migrationFile.UpFile)
|
||||
counter -= 1
|
||||
counter--
|
||||
} else if d == direction.Down && migrationFile.Version <= version && migrationFile.DownFile != nil {
|
||||
files = append(files, *migrationFile.DownFile)
|
||||
counter -= 1
|
||||
counter--
|
||||
}
|
||||
} else {
|
||||
break
|
||||
@@ -258,7 +259,7 @@ func parseFilenameSchema(filename string, filenameRegex *regexp.Regexp) (version
|
||||
|
||||
version, err = strconv.ParseUint(matches[1], 10, 0)
|
||||
if err != nil {
|
||||
return 0, "", 0, errors.New(fmt.Sprintf("Unable to parse version '%v' in filename schema", matches[0]))
|
||||
return 0, "", 0, fmt.Errorf("Unable to parse version '%v' in filename schema", matches[0])
|
||||
}
|
||||
|
||||
if matches[3] == "up" {
|
||||
@@ -266,7 +267,7 @@ func parseFilenameSchema(filename string, filenameRegex *regexp.Regexp) (version
|
||||
} else if matches[3] == "down" {
|
||||
d = direction.Down
|
||||
} else {
|
||||
return 0, "", 0, errors.New(fmt.Sprintf("Unable to parse up|down '%v' in filename schema", matches[3]))
|
||||
return 0, "", 0, fmt.Errorf("Unable to parse up|down '%v' in filename schema", matches[3])
|
||||
}
|
||||
|
||||
return version, matches[2], d, nil
|
||||
@@ -333,7 +334,7 @@ func LinesBeforeAndAfter(data []byte, line, before, after int, lineNumbers bool)
|
||||
lNew = append([]byte(lineCounterStr+": "), lNew...)
|
||||
}
|
||||
newLines = append(newLines, lNew)
|
||||
lineCounter += 1
|
||||
lineCounter++
|
||||
}
|
||||
|
||||
return bytes.Join(newLines, []byte("\n"))
|
||||
|
||||
+6
-4
@@ -1,11 +1,12 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
)
|
||||
|
||||
func TestParseFilenameSchema(t *testing.T) {
|
||||
@@ -61,7 +62,7 @@ func TestFiles(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(tmpdir, "nonsense.txt"), nil, 0755); err != nil {
|
||||
if err = ioutil.WriteFile(path.Join(tmpdir, "nonsense.txt"), nil, 0755); err != nil {
|
||||
t.Fatal("Unable to write files in tmpdir", err)
|
||||
}
|
||||
ioutil.WriteFile(path.Join(tmpdir, "002_migrationfile.up.sql"), nil, 0755)
|
||||
@@ -133,7 +134,7 @@ func TestFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
// test read
|
||||
if err := files[4].DownFile.ReadContent(); err != nil {
|
||||
if err = files[4].DownFile.ReadContent(); err != nil {
|
||||
t.Error("Unable to read file", err)
|
||||
}
|
||||
if files[4].DownFile.Content == nil {
|
||||
@@ -169,7 +170,8 @@ func TestFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
rangeFiles, err := files.From(test.from, test.relative)
|
||||
var rangeFiles Files
|
||||
rangeFiles, err = files.From(test.from, test.relative)
|
||||
if err != nil {
|
||||
t.Error("Unable to fetch range:", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package main is the CLI.
|
||||
// You can use the CLI via Terminal.
|
||||
// import "github.com/mattes/migrate/migrate" for usage within Go.
|
||||
// import "gopkg.in/mattes/migrate.v1/migrate" for usage within Go.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -11,18 +11,19 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
_ "github.com/mattes/migrate/driver/bash"
|
||||
_ "github.com/mattes/migrate/driver/cassandra"
|
||||
_ "github.com/mattes/migrate/driver/crate"
|
||||
_ "github.com/mattes/migrate/driver/mysql"
|
||||
_ "github.com/mattes/migrate/driver/neo4j"
|
||||
_ "github.com/mattes/migrate/driver/postgres"
|
||||
_ "github.com/mattes/migrate/driver/ql"
|
||||
_ "github.com/mattes/migrate/driver/sqlite3"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/bash"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/cassandra"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/crate"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/mssql"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/mysql"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/neo4j"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/postgres"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/ql"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/sqlite3"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
var url = flag.String("url", os.Getenv("MIGRATE_URL"), "")
|
||||
@@ -176,32 +177,31 @@ func writePipe(pipe chan interface{}) (ok bool) {
|
||||
case item, more := <-pipe:
|
||||
if !more {
|
||||
return okFlag
|
||||
} else {
|
||||
switch item.(type) {
|
||||
}
|
||||
switch item.(type) {
|
||||
|
||||
case string:
|
||||
fmt.Println(item.(string))
|
||||
case string:
|
||||
fmt.Println(item.(string))
|
||||
|
||||
case error:
|
||||
case error:
|
||||
c := color.New(color.FgRed)
|
||||
c.Printf("%s\n\n", item.(error).Error())
|
||||
okFlag = false
|
||||
|
||||
case file.File:
|
||||
f := item.(file.File)
|
||||
if f.Direction == direction.Up {
|
||||
c := color.New(color.FgGreen)
|
||||
c.Print(">")
|
||||
} else if f.Direction == direction.Down {
|
||||
c := color.New(color.FgRed)
|
||||
c.Println(item.(error).Error(), "\n")
|
||||
okFlag = false
|
||||
|
||||
case file.File:
|
||||
f := item.(file.File)
|
||||
if f.Direction == direction.Up {
|
||||
c := color.New(color.FgGreen)
|
||||
c.Print(">")
|
||||
} else if f.Direction == direction.Down {
|
||||
c := color.New(color.FgRed)
|
||||
c.Print("<")
|
||||
}
|
||||
fmt.Printf(" %s\n", f.FileName)
|
||||
|
||||
default:
|
||||
text := fmt.Sprint(item)
|
||||
fmt.Println(text)
|
||||
c.Print("<")
|
||||
}
|
||||
fmt.Printf(" %s\n", f.FileName)
|
||||
|
||||
default:
|
||||
text := fmt.Sprint(item)
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// Package direction just holds convenience constants for Up and Down migrations.
|
||||
package direction
|
||||
|
||||
// Direction - type that indicates direction of migration(up or down)
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
Up Direction = +1
|
||||
Down = -1
|
||||
// Up - up migration
|
||||
Up Direction = +1
|
||||
// Down - down migration
|
||||
Down Direction = -1
|
||||
)
|
||||
|
||||
+23
-24
@@ -12,10 +12,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattes/migrate/driver"
|
||||
"github.com/mattes/migrate/file"
|
||||
"github.com/mattes/migrate/migrate/direction"
|
||||
pipep "github.com/mattes/migrate/pipe"
|
||||
"gopkg.in/mattes/migrate.v1/driver"
|
||||
"gopkg.in/mattes/migrate.v1/file"
|
||||
"gopkg.in/mattes/migrate.v1/migrate/direction"
|
||||
pipep "gopkg.in/mattes/migrate.v1/pipe"
|
||||
)
|
||||
|
||||
// Up applies all available migrations
|
||||
@@ -51,13 +51,12 @@ func Up(pipe chan interface{}, url, migrationsPath string) {
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
} else {
|
||||
if err := d.Close(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
}
|
||||
if err := d.Close(); err != nil {
|
||||
pipe <- err
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// UpSync is synchronous version of Up
|
||||
@@ -101,13 +100,12 @@ func Down(pipe chan interface{}, url, migrationsPath string) {
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
} else {
|
||||
if err2 := d.Close(); err2 != nil {
|
||||
pipe <- err2
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
}
|
||||
if err2 := d.Close(); err2 != nil {
|
||||
pipe <- err2
|
||||
}
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// DownSync is synchronous version of Down
|
||||
@@ -129,9 +127,8 @@ func Redo(pipe chan interface{}, url, migrationsPath string) {
|
||||
if ok := pipep.WaitAndRedirect(pipe1, pipe, signals); !ok {
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
} else {
|
||||
go Migrate(pipe, url, migrationsPath, +1)
|
||||
}
|
||||
go Migrate(pipe, url, migrationsPath, +1)
|
||||
}
|
||||
|
||||
// RedoSync is synchronous version of Redo
|
||||
@@ -153,9 +150,8 @@ func Reset(pipe chan interface{}, url, migrationsPath string) {
|
||||
if ok := pipep.WaitAndRedirect(pipe1, pipe, signals); !ok {
|
||||
go pipep.Close(pipe, nil)
|
||||
return
|
||||
} else {
|
||||
go Up(pipe, url, migrationsPath)
|
||||
}
|
||||
go Up(pipe, url, migrationsPath)
|
||||
}
|
||||
|
||||
// ResetSync is synchronous version of Reset
|
||||
@@ -221,17 +217,20 @@ func Version(url, migrationsPath string) (version uint64, err error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
err = d.Close()
|
||||
}()
|
||||
return d.Version()
|
||||
}
|
||||
|
||||
// Create creates new migration files on disk
|
||||
func Create(url, migrationsPath, name string) (*file.MigrationFile, error) {
|
||||
d, err := driver.New(url)
|
||||
ext, err := driver.FilenameExtensionFromURL(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files, err := file.ReadMigrationFiles(migrationsPath, file.FilenameRegex(d.FilenameExtension()))
|
||||
files, err := file.ReadMigrationFiles(migrationsPath, file.FilenameRegex(ext))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -258,14 +257,14 @@ func Create(url, migrationsPath, name string) (*file.MigrationFile, error) {
|
||||
Version: version,
|
||||
UpFile: &file.File{
|
||||
Path: migrationsPath,
|
||||
FileName: fmt.Sprintf(filenamef, versionStr, name, "up", d.FilenameExtension()),
|
||||
FileName: fmt.Sprintf(filenamef, versionStr, name, "up", ext),
|
||||
Name: name,
|
||||
Content: []byte(""),
|
||||
Direction: direction.Up,
|
||||
},
|
||||
DownFile: &file.File{
|
||||
Path: migrationsPath,
|
||||
FileName: fmt.Sprintf(filenamef, versionStr, name, "down", d.FilenameExtension()),
|
||||
FileName: fmt.Sprintf(filenamef, versionStr, name, "down", ext),
|
||||
Name: name,
|
||||
Content: []byte(""),
|
||||
Direction: direction.Down,
|
||||
|
||||
+60
-60
@@ -8,19 +8,19 @@ import (
|
||||
|
||||
// Ensure imports for each driver we wish to test
|
||||
|
||||
_ "github.com/mattes/migrate/driver/postgres"
|
||||
_ "github.com/mattes/migrate/driver/sqlite3"
|
||||
_ "github.com/mattes/migrate/driver/ql"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/postgres"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/ql"
|
||||
_ "gopkg.in/mattes/migrate.v1/driver/sqlite3"
|
||||
)
|
||||
|
||||
// Add Driver URLs here to test basic Up, Down, .. functions.
|
||||
var driverUrls = []string{
|
||||
var driverURLs = []string{
|
||||
"postgres://postgres@" + os.Getenv("POSTGRES_PORT_5432_TCP_ADDR") + ":" + os.Getenv("POSTGRES_PORT_5432_TCP_PORT") + "/template1?sslmode=disable",
|
||||
"ql+file://./test.db",
|
||||
}
|
||||
|
||||
func tearDown(driverUrl, tmpdir string) {
|
||||
DownSync(driverUrl, tmpdir)
|
||||
func tearDown(driverURL, tmpdir string) {
|
||||
DownSync(driverURL, tmpdir)
|
||||
os.RemoveAll(tmpdir)
|
||||
}
|
||||
|
||||
@@ -28,18 +28,18 @@ func TestCreate(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
if _, err := Create(driverUrl, tmpdir, "test_migration"); err != nil {
|
||||
if _, err = Create(driverURL, tmpdir, "test_migration"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Create(driverUrl, tmpdir, "another migration"); err != nil {
|
||||
if _, err = Create(driverURL, tmpdir, "another migration"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -83,25 +83,25 @@ func TestReset(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
Create(driverUrl, tmpdir, "migration1")
|
||||
f, err := Create(driverUrl, tmpdir, "migration2")
|
||||
Create(driverURL, tmpdir, "migration1")
|
||||
f, err := Create(driverURL, tmpdir, "migration2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := ResetSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != f.Version {
|
||||
t.Fatalf("Expected version %v, got %v", version, f.Version)
|
||||
@@ -113,36 +113,36 @@ func TestDown(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
initVersion, _ := Version(driverUrl, tmpdir)
|
||||
initVersion, _ := Version(driverURL, tmpdir)
|
||||
|
||||
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
|
||||
firstMigration, _ := Create(driverURL, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverURL, tmpdir, "migration2")
|
||||
|
||||
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
|
||||
|
||||
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := ResetSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != secondMigration.Version {
|
||||
t.Fatalf("Expected version %v, got %v", version, secondMigration.Version)
|
||||
}
|
||||
|
||||
if err, ok := DownSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := DownSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != 0 {
|
||||
t.Fatalf("Expected 0, got %v", version)
|
||||
@@ -154,36 +154,36 @@ func TestUp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
initVersion, _ := Version(driverUrl, tmpdir)
|
||||
initVersion, _ := Version(driverURL, tmpdir)
|
||||
|
||||
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
|
||||
firstMigration, _ := Create(driverURL, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverURL, tmpdir, "migration2")
|
||||
|
||||
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
|
||||
|
||||
if err, ok := DownSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := DownSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != initVersion {
|
||||
t.Fatalf("Expected initial version %v, got %v", initVersion, version)
|
||||
}
|
||||
|
||||
if err, ok := UpSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := UpSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != secondMigration.Version {
|
||||
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
|
||||
@@ -195,36 +195,36 @@ func TestRedo(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
initVersion, _ := Version(driverUrl, tmpdir)
|
||||
initVersion, _ := Version(driverURL, tmpdir)
|
||||
|
||||
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
|
||||
firstMigration, _ := Create(driverURL, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverURL, tmpdir, "migration2")
|
||||
|
||||
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
|
||||
|
||||
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := ResetSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != secondMigration.Version {
|
||||
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
|
||||
}
|
||||
|
||||
if err, ok := RedoSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := RedoSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != secondMigration.Version {
|
||||
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
|
||||
@@ -236,46 +236,46 @@ func TestMigrate(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
for _, driverUrl := range driverUrls {
|
||||
t.Logf("Test driver: %s", driverUrl)
|
||||
for _, driverURL := range driverURLs {
|
||||
t.Logf("Test driver: %s", driverURL)
|
||||
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown(driverUrl, tmpdir)
|
||||
defer tearDown(driverURL, tmpdir)
|
||||
|
||||
initVersion, _ := Version(driverUrl, tmpdir)
|
||||
initVersion, _ := Version(driverURL, tmpdir)
|
||||
|
||||
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
|
||||
firstMigration, _ := Create(driverURL, tmpdir, "migration1")
|
||||
secondMigration, _ := Create(driverURL, tmpdir, "migration2")
|
||||
|
||||
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
|
||||
|
||||
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
|
||||
if err, ok := ResetSync(driverURL, tmpdir); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != secondMigration.Version {
|
||||
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
|
||||
}
|
||||
|
||||
if err, ok := MigrateSync(driverUrl, tmpdir, -2); !ok {
|
||||
if err, ok := MigrateSync(driverURL, tmpdir, -2); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != 0 {
|
||||
t.Fatalf("Expected 0, got %v", version)
|
||||
}
|
||||
|
||||
if err, ok := MigrateSync(driverUrl, tmpdir, +1); !ok {
|
||||
if err, ok := MigrateSync(driverURL, tmpdir, +1); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if version, err := Version(driverUrl, tmpdir); err != nil {
|
||||
if version, err := Version(driverURL, tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if version != firstMigration.Version {
|
||||
t.Fatalf("Expected first version %v, got %v", firstMigration.Version, version)
|
||||
|
||||
+10
-12
@@ -32,7 +32,7 @@ func WaitAndRedirect(pipe, redirectPipe chan interface{}, interrupt chan os.Sign
|
||||
select {
|
||||
|
||||
case <-interrupt:
|
||||
interruptsReceived += 1
|
||||
interruptsReceived++
|
||||
if interruptsReceived > 1 {
|
||||
os.Exit(5)
|
||||
} else {
|
||||
@@ -43,12 +43,11 @@ func WaitAndRedirect(pipe, redirectPipe chan interface{}, interrupt chan os.Sign
|
||||
case item, ok := <-pipe:
|
||||
if !ok {
|
||||
return !errorReceived && interruptsReceived == 0
|
||||
} else {
|
||||
redirectPipe <- item
|
||||
switch item.(type) {
|
||||
case error:
|
||||
errorReceived = true
|
||||
}
|
||||
}
|
||||
redirectPipe <- item
|
||||
switch item.(type) {
|
||||
case error:
|
||||
errorReceived = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,11 +65,10 @@ func ReadErrors(pipe chan interface{}) []error {
|
||||
case item, ok := <-pipe:
|
||||
if !ok {
|
||||
return err
|
||||
} else {
|
||||
switch item.(type) {
|
||||
case error:
|
||||
err = append(err, item.(error))
|
||||
}
|
||||
}
|
||||
switch item.(type) {
|
||||
case error:
|
||||
err = append(err, item.(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
package main
|
||||
|
||||
const Version string = "1.3.0"
|
||||
// Version - version of tool
|
||||
const Version string = "1.3.1"
|
||||
|
||||
Reference in New Issue
Block a user