Compare commits

...
36 Commits
Author SHA1 Message Date
Matthias KadenbachandGitHub dfe3ec0fce Merge pull request #198 from rmulley/travis-go-1.8
TravisCI Updates
2017-07-09 11:00:29 -07:00
Matthias KadenbachandGitHub 01b25ee18b Merge branch 'v1' into travis-go-1.8 2017-07-09 10:59:35 -07:00
Matthias KadenbachandGitHub afc973c4e5 Merge pull request #216 from thingful/v1.3.1-neo4j-https
Register neo4j driver using a pseudo `neo4js` protocol scheme.
2017-05-09 10:37:06 -07:00
Sam Mulube 250543a561 Register neo4j driver using a pseudo neo4js protocol scheme.
Neo4J supports both `http` and `https` connections, however `https`
connections weren't supported previously as the `neo4j` URL scheme
currently used to identify the driver type is always substituted for
just `http`.

This PR also registers the driver under an alternate key: `neo4js` which
given the way the scheme substitution happens, should allow the driver
to correctly create an `https` connection URL. This will allow the
library to be used for cases where we don't have access to the plain
`http` endpoint.
2017-05-09 09:54:37 +01:00
Matthias KadenbachandGitHub 1dffba9fa5 Merge pull request #212 from mattes/v1-gopkg
v1: github.com/mattes/migrate -> gopkg.in/mattes/migrate.v1
2017-05-06 16:25:42 -07:00
Matthias Kadenbach 964e6bc070 rewrite in readmes 2017-05-06 16:18:32 -07:00
Matthias Kadenbach 1887c0f3e3 rewrite paths 2017-05-06 16:13:08 -07:00
Matthias Kadenbach 62dba4f8ea update travis branch 2017-04-25 12:02:57 -07:00
Matthias KadenbachandGitHub 78ca4eb007 Merge pull request #190 from alaczi/mssql_support
MSSQL support
2017-04-25 11:35:40 -07:00
Andras Laczi d21443f74b Remove go1.4 from travis as the ql package is not compatible and breaks the build 2017-04-25 09:35:03 +01:00
Andras Laczi 7f4000c0d6 Add wait-retry to mssql test 2017-04-24 13:15:17 +01:00
Andras Laczi ecd74e1ce6 Add go 1.8 to travis config
Fix getting the latest version
2017-04-24 13:15:12 +01:00
Matthias Kadenbach 9f14676773 Update Readme 2017-04-20 15:04:54 -07:00
Matthias KadenbachandGitHub 7dde434714 Update version.go 2017-04-20 11:50:49 -07:00
Robert Mulley fff063351d Pull latest patch versions for each go minor version 2017-04-13 13:01:59 -06:00
Robert Mulley 5498e70562 Bump docker-compose to latest, 1.11.2 2017-04-13 12:54:35 -06:00
Robert Mulley 0da98940d6 Add go 1.8 to test matrix 2017-04-13 12:44:27 -06:00
Matthias KadenbachandGitHub 4cfc044489 Merge pull request #193 from Arkq/master
Support for alternative postgres URI scheme
2017-04-03 10:33:35 -07:00
Arkadiusz Bokowy 83c703f09f Support for alternative postgres URI scheme
According to the PostgreSQL documentation (section 32.1.1.2), postgres
library supports two URI schemes: postgresql:// and postgres://

Reference:
https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
2017-04-03 09:15:25 +02:00
Andras Laczi b7845a779e Add MSSQL support 2017-03-30 15:32:14 +01:00
Matthias KadenbachandGitHub 09640b136e Merge pull request #189 from nathj07/issue-188-sqlite-err-cast
fixing error cast for sqlite3
2017-03-29 11:48:54 -07:00
Nathan Davies 7ca71e567d fixing error cast for sqlite3
Seems the sqlite3 packaged reverted to using non-pointer Error. This patch corrects the use of it here.
2017-03-29 14:06:38 +01:00
Matthias KadenbachandGitHub c7653285b2 Merge pull request #185 from adrianduke/master
driver/sqlite3: Fix for upstream changes
2017-03-20 10:09:33 -07:00
Adrian Duke f4979054e2 driver/sqlite3 fix for upstream change sqlite3.Error{} Error() now takes a pointer receiver 2017-03-20 16:07:01 +00:00
Matthias KadenbachandGitHub 305a63a447 Merge pull request #181 from chrisaxiom/master
Call Close() in Version()
2017-03-02 13:39:55 -08:00
Chris S 528bc5bbbb close driver 2017-03-02 12:48:31 -08:00
Matthias KadenbachandGitHub 83e27c8b88 Merge pull request #176 from Synesis-LLC/stage
Golint. Rewrite cassandra driver
2017-02-27 10:12:48 -08:00
Pavel Sapezhko e8bfb1b2b3 Merge remote-tracking branch 'upstream/master' into stage 2017-02-27 17:56:15 +03:00
Matthias KadenbachandGitHub 2cd9bfcd04 Merge pull request #173 from sekimura/govet
Fix for go vet
2017-02-21 10:35:02 -08:00
Pavel Sapezhko 21ccec2aa1 allow create migration files without db connection 2017-02-21 09:13:59 +03:00
Masa Sekimura fe6711645a Fix for go vet
Avoid getting caught by go vet for using "\n" with Println

	main.go:187: Println call ends with newline
2017-02-20 23:39:51 +00:00
Pavel Sapezhko adf1cff659 drivers: cassandra: rewrite 2017-02-17 17:59:38 +03:00
Pavel Sapezhko da12e27062 fix some lint errors 2017-02-17 13:06:26 +03:00
Matthias KadenbachandGitHub d2ad6f8ecf Update README.md 2017-02-10 02:42:46 -08:00
Matthias KadenbachandGitHub bbb58aca8d Merge pull request #146 from FreGalle/postgres-version-table-check-schema
Postgres: version table existence check in current schema
2017-02-04 10:17:41 -08:00
Frederik Galle 5aed294d25 Check version table existence for current schema 2017-02-03 12:47:24 +01:00
41 changed files with 771 additions and 415 deletions
+7 -5
View File
@@ -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
+15 -10
View File
@@ -1,19 +1,23 @@
# You are looking at a deprecated version.
# Use the [latest version](https://github.com/mattes/migrate) instead.
---
# migrate
[![Build Status](https://travis-ci.org/mattes/migrate.svg?branch=master)](https://travis-ci.org/mattes/migrate)
[![GoDoc](https://godoc.org/github.com/mattes/migrate?status.svg)](https://godoc.org/github.com/mattes/migrate)
[![Build Status](https://travis-ci.org/mattes/migrate.svg?branch=v1)](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
View File
@@ -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"
+1 -1
View File
@@ -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
View File
@@ -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 {
+43 -58
View File
@@ -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() {
+12 -10
View File
@@ -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)
}
+1 -1
View File
@@ -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
```
```
+5 -5
View File
@@ -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)
+3 -3
View File
@@ -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
View File
@@ -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) {
+4 -4
View File
@@ -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"
)
+7 -6
View File
@@ -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
+7 -6
View File
@@ -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
+22
View File
@@ -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
+128
View File
@@ -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{})
}
+168
View File
@@ -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)
}
}
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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 {
+3 -3
View File
@@ -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().
+1 -1
View File
@@ -17,4 +17,4 @@ migrate help # for more info
```
## Author
* Carlos Forero, https://github.com/carlosforero
* Carlos Forero, https://github.com/carlosforero
+114 -104
View File
@@ -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{})
}
+3 -3
View File
@@ -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().
+7 -5
View File
@@ -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)
}
+7 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+5 -5
View File
@@ -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 {
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+35 -35
View File
@@ -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)
}
}
}
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,4 @@
package main
const Version string = "1.3.0"
// Version - version of tool
const Version string = "1.3.1"