Capabilities handling, database.
This commit is contained in:
11
vendor/github.com/pressly/goose/.gitignore
generated
vendored
Normal file
11
vendor/github.com/pressly/goose/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.DS_Store
|
||||
cmd/goose/goose*
|
||||
*.swp
|
||||
*.test
|
||||
|
||||
# Files output by tests
|
||||
bin
|
||||
go.db
|
||||
sql.db
|
||||
|
||||
|
11
vendor/github.com/pressly/goose/.travis.yml
generated
vendored
Normal file
11
vendor/github.com/pressly/goose/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.8
|
||||
|
||||
install:
|
||||
- go get github.com/golang/dep/cmd/dep
|
||||
- dep ensure
|
||||
|
||||
script:
|
||||
- go test
|
11
vendor/github.com/pressly/goose/Gopkg.toml
generated
vendored
Normal file
11
vendor/github.com/pressly/goose/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
[[constraint]]
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
version = "^1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
version = "^1.2.0"
|
21
vendor/github.com/pressly/goose/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pressly/goose/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Original work Copyright (c) 2012 Liam Staskawicz
|
||||
Modified work Copyright (c) 2016 Vojtech Vitek
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
vendor/github.com/pressly/goose/Makefile
generated
vendored
Normal file
9
vendor/github.com/pressly/goose/Makefile
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
.PHONY: dist
|
||||
dist:
|
||||
@mkdir -p ./bin
|
||||
@rm -f ./bin/*
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./bin/goose-darwin64 ./cmd/goose
|
||||
GOOS=linux GOARCH=amd64 go build -o ./bin/goose-linux64 ./cmd/goose
|
||||
GOOS=linux GOARCH=386 go build -o ./bin/goose-linux386 ./cmd/goose
|
||||
GOOS=windows GOARCH=amd64 go build -o ./bin/goose-windows64.exe ./cmd/goose
|
||||
GOOS=windows GOARCH=386 go build -o ./bin/goose-windows386.exe ./cmd/goose
|
256
vendor/github.com/pressly/goose/README.md
generated
vendored
Normal file
256
vendor/github.com/pressly/goose/README.md
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
# goose
|
||||
|
||||
Goose is a database migration tool. Manage your database schema by creating incremental SQL changes or Go functions.
|
||||
|
||||
[![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis]
|
||||
|
||||
### Goals of this fork
|
||||
|
||||
`github.com/pressly/goose` is a fork of `bitbucket.org/liamstask/goose` with the following changes:
|
||||
- No config files
|
||||
- [Default goose binary](./cmd/goose/main.go) can migrate SQL files only
|
||||
- Go migrations:
|
||||
- We don't `go build` Go migrations functions on-the-fly
|
||||
from within the goose binary
|
||||
- Instead, we let you
|
||||
[create your own custom goose binary](examples/go-migrations),
|
||||
register your Go migration functions explicitly and run complex
|
||||
migrations with your own `*sql.DB` connection
|
||||
- Go migration functions let you run your code within
|
||||
an SQL transaction, if you use the `*sql.Tx` argument
|
||||
- The goose pkg is decoupled from the binary:
|
||||
- goose pkg doesn't register any SQL drivers anymore,
|
||||
thus no driver `panic()` conflict within your codebase!
|
||||
- goose pkg doesn't have any vendor dependencies anymore
|
||||
- We use timestamped migrations by default but recommend a hybrid approach of using timestamps in the development process and sequential versions in production.
|
||||
|
||||
# Install
|
||||
|
||||
$ go get -u github.com/pressly/goose/cmd/goose
|
||||
|
||||
This will install the `goose` binary to your `$GOPATH/bin` directory.
|
||||
|
||||
For a lite version of the binary without DB connection dependent commands, use the exclusive build tags:
|
||||
|
||||
$ go build -tags='no_mysql no_sqlite no_psql' -i -o goose ./cmd/goose
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
Usage: goose [OPTIONS] DRIVER DBSTRING COMMAND
|
||||
|
||||
Drivers:
|
||||
postgres
|
||||
mysql
|
||||
sqlite3
|
||||
redshift
|
||||
|
||||
Commands:
|
||||
up Migrate the DB to the most recent version available
|
||||
up-to VERSION Migrate the DB to a specific VERSION
|
||||
down Roll back the version by 1
|
||||
down-to VERSION Roll back to a specific VERSION
|
||||
redo Re-run the latest migration
|
||||
status Dump the migration status for the current DB
|
||||
version Print the current version of the database
|
||||
create NAME [sql|go] Creates new migration file with the current timestamp
|
||||
|
||||
Options:
|
||||
-dir string
|
||||
directory with migration files (default ".")
|
||||
|
||||
Examples:
|
||||
goose sqlite3 ./foo.db status
|
||||
goose sqlite3 ./foo.db create init sql
|
||||
goose sqlite3 ./foo.db create add_some_column sql
|
||||
goose sqlite3 ./foo.db create fetch_user_data go
|
||||
goose sqlite3 ./foo.db up
|
||||
|
||||
goose postgres "user=postgres dbname=postgres sslmode=disable" status
|
||||
goose mysql "user:password@/dbname?parseTime=true" status
|
||||
goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" status
|
||||
goose tidb "user:password@/dbname?parseTime=true" status
|
||||
```
|
||||
## create
|
||||
|
||||
Create a new SQL migration.
|
||||
|
||||
$ goose create add_some_column sql
|
||||
$ Created new file: 20170506082420_add_some_column.sql
|
||||
|
||||
Edit the newly created file to define the behavior of your migration.
|
||||
|
||||
You can also create a Go migration, if you then invoke it with [your own goose binary](#go-migrations):
|
||||
|
||||
$ goose create fetch_user_data go
|
||||
$ Created new file: 20170506082421_fetch_user_data.go
|
||||
|
||||
## up
|
||||
|
||||
Apply all available migrations.
|
||||
|
||||
$ goose up
|
||||
$ OK 001_basics.sql
|
||||
$ OK 002_next.sql
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## up-to
|
||||
|
||||
Migrate up to a specific version.
|
||||
|
||||
$ goose up-to 20170506082420
|
||||
$ OK 20170506082420_create_table.sql
|
||||
|
||||
## down
|
||||
|
||||
Roll back a single migration from the current version.
|
||||
|
||||
$ goose down
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## down-to
|
||||
|
||||
Roll back migrations to a specific version.
|
||||
|
||||
$ goose down-to 20170506082527
|
||||
$ OK 20170506082527_alter_column.sql
|
||||
|
||||
## redo
|
||||
|
||||
Roll back the most recently applied migration, then run it again.
|
||||
|
||||
$ goose redo
|
||||
$ OK 003_and_again.go
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## status
|
||||
|
||||
Print the status of all migrations:
|
||||
|
||||
$ goose status
|
||||
$ Applied At Migration
|
||||
$ =======================================
|
||||
$ Sun Jan 6 11:25:03 2013 -- 001_basics.sql
|
||||
$ Sun Jan 6 11:25:03 2013 -- 002_next.sql
|
||||
$ Pending -- 003_and_again.go
|
||||
|
||||
Note: for MySQL [parseTime flag](https://github.com/go-sql-driver/mysql#parsetime) must be enabled.
|
||||
|
||||
## version
|
||||
|
||||
Print the current version of the database:
|
||||
|
||||
$ goose version
|
||||
$ goose: version 002
|
||||
|
||||
# Migrations
|
||||
|
||||
goose supports migrations written in SQL or in Go.
|
||||
|
||||
## SQL Migrations
|
||||
|
||||
A sample SQL migration looks like:
|
||||
|
||||
```sql
|
||||
-- +goose Up
|
||||
CREATE TABLE post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE post;
|
||||
```
|
||||
|
||||
Notice the annotations in the comments. Any statements following `-- +goose Up` will be executed as part of a forward migration, and any statements following `-- +goose Down` will be executed as part of a rollback.
|
||||
|
||||
By default, all migrations are run within a transaction. Some statements like `CREATE DATABASE`, however, cannot be run within a transaction. You may optionally add `-- +goose NO TRANSACTION` to the top of your migration
|
||||
file in order to skip transactions within that specific migration file. Both Up and Down migrations within this file will be run without transactions.
|
||||
|
||||
By default, SQL statements are delimited by semicolons - in fact, query statements must end with a semicolon to be properly recognized by goose.
|
||||
|
||||
More complex statements (PL/pgSQL) that have semicolons within them must be annotated with `-- +goose StatementBegin` and `-- +goose StatementEnd` to be properly recognized. For example:
|
||||
|
||||
```sql
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE )
|
||||
returns void AS $$
|
||||
DECLARE
|
||||
create_query text;
|
||||
BEGIN
|
||||
FOR create_query IN SELECT
|
||||
'CREATE TABLE IF NOT EXISTS histories_'
|
||||
|| TO_CHAR( d, 'YYYY_MM' )
|
||||
|| ' ( CHECK( created_at >= timestamp '''
|
||||
|| TO_CHAR( d, 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' AND created_at < timestamp '''
|
||||
|| TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' ) ) inherits ( histories );'
|
||||
FROM generate_series( $1, $2, '1 month' ) AS d
|
||||
LOOP
|
||||
EXECUTE create_query;
|
||||
END LOOP; -- LOOP END
|
||||
END; -- FUNCTION END
|
||||
$$
|
||||
language plpgsql;
|
||||
-- +goose StatementEnd
|
||||
```
|
||||
|
||||
## Go Migrations
|
||||
|
||||
1. Create your own goose binary, see [example](./examples/go-migrations)
|
||||
2. Import `github.com/pressly/goose`
|
||||
3. Register your migration functions
|
||||
4. Run goose command, ie. `goose.Up(db *sql.DB, dir string)`
|
||||
|
||||
A [sample Go migration 00002_users_add_email.go file](./example/migrations-go/00002_rename_root.go) looks like:
|
||||
|
||||
```go
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/pressly/goose"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goose.AddMigration(Up, Down)
|
||||
}
|
||||
|
||||
func Up(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
# Hybrid Versioning
|
||||
Please, read the [versioning problem](https://github.com/pressly/goose/issues/63#issuecomment-428681694) first.
|
||||
|
||||
We strongly recommend adopting a hybrid versioning approach, using both timestamps and sequential numbers. Migrations created during the development process are timestamped and sequential versions are ran on production. We believe this method will prevent the problem of conflicting versions when writing software in a team environment.
|
||||
|
||||
To help you adopt this approach, `create` will use the current timestamp as the migration version. When you're ready to deploy your migrations in a production environment, we also provide a helpful `fix` command to convert your migrations into sequential order, while preserving the timestamp ordering. We recommend running `fix` in the CI pipeline, and only when the migrations are ready for production.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT License](./LICENSE)
|
||||
|
||||
[GoDoc]: https://godoc.org/github.com/pressly/goose
|
||||
[GoDoc Widget]: https://godoc.org/github.com/pressly/goose?status.svg
|
||||
[Travis]: https://travis-ci.org/pressly/goose
|
||||
[Travis Widget]: https://travis-ci.org/pressly/goose.svg?branch=master
|
88
vendor/github.com/pressly/goose/create.go
generated
vendored
Normal file
88
vendor/github.com/pressly/goose/create.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Create writes a new blank migration file.
|
||||
func CreateWithTemplate(db *sql.DB, dir string, migrationTemplate *template.Template, name, migrationType string) error {
|
||||
version := time.Now().Format(timestampFormat)
|
||||
filename := fmt.Sprintf("%v_%v.%v", version, name, migrationType)
|
||||
|
||||
fpath := filepath.Join(dir, filename)
|
||||
|
||||
tmpl := sqlMigrationTemplate
|
||||
if migrationType == "go" {
|
||||
tmpl = goSQLMigrationTemplate
|
||||
}
|
||||
|
||||
if migrationTemplate != nil {
|
||||
tmpl = migrationTemplate
|
||||
}
|
||||
|
||||
path, err := writeTemplateToFile(fpath, tmpl, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Created new file: %s\n", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create writes a new blank migration file.
|
||||
func Create(db *sql.DB, dir, name, migrationType string) error {
|
||||
return CreateWithTemplate(db, dir, nil, name, migrationType)
|
||||
}
|
||||
|
||||
func writeTemplateToFile(path string, t *template.Template, version string) (string, error) {
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("failed to create file: %v already exists", path)
|
||||
}
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = t.Execute(f, version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
var sqlMigrationTemplate = template.Must(template.New("goose.sql-migration").Parse(`-- +goose Up
|
||||
-- SQL in this section is executed when the migration is applied.
|
||||
|
||||
-- +goose Down
|
||||
-- SQL in this section is executed when the migration is rolled back.
|
||||
`))
|
||||
|
||||
var goSQLMigrationTemplate = template.Must(template.New("goose.go-migration").Parse(`package migration
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/pressly/goose"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goose.AddMigration(Up{{.}}, Down{{.}})
|
||||
}
|
||||
|
||||
func Up{{.}}(tx *sql.Tx) error {
|
||||
// This code is executed when the migration is applied.
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down{{.}}(tx *sql.Tx) error {
|
||||
// This code is executed when the migration is rolled back.
|
||||
return nil
|
||||
}
|
||||
`))
|
211
vendor/github.com/pressly/goose/dialect.go
generated
vendored
Normal file
211
vendor/github.com/pressly/goose/dialect.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SQLDialect abstracts the details of specific SQL dialects
|
||||
// for goose's few SQL specific statements
|
||||
type SQLDialect interface {
|
||||
createVersionTableSQL() string // sql string to create the db version table
|
||||
insertVersionSQL() string // sql string to insert the initial version table row
|
||||
deleteVersionSQL() string // sql string to delete version
|
||||
dbVersionQuery(db *sql.DB) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
var dialect SQLDialect = &PostgresDialect{}
|
||||
|
||||
// GetDialect gets the SQLDialect
|
||||
func GetDialect() SQLDialect {
|
||||
return dialect
|
||||
}
|
||||
|
||||
// SetDialect sets the SQLDialect
|
||||
func SetDialect(d string) error {
|
||||
switch d {
|
||||
case "postgres":
|
||||
dialect = &PostgresDialect{}
|
||||
case "mysql":
|
||||
dialect = &MySQLDialect{}
|
||||
case "sqlite3":
|
||||
dialect = &Sqlite3Dialect{}
|
||||
case "redshift":
|
||||
dialect = &RedshiftDialect{}
|
||||
case "tidb":
|
||||
dialect = &TiDBDialect{}
|
||||
default:
|
||||
return fmt.Errorf("%q: unknown dialect", d)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// Postgres
|
||||
////////////////////////////
|
||||
|
||||
// PostgresDialect struct.
|
||||
type PostgresDialect struct{}
|
||||
|
||||
func (pg PostgresDialect) createVersionTableSQL() string {
|
||||
return fmt.Sprintf(`CREATE TABLE %s (
|
||||
id serial NOT NULL,
|
||||
version_id bigint NOT NULL,
|
||||
is_applied boolean NOT NULL,
|
||||
tstamp timestamp NULL default now(),
|
||||
PRIMARY KEY(id)
|
||||
);`, TableName())
|
||||
}
|
||||
|
||||
func (pg PostgresDialect) insertVersionSQL() string {
|
||||
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES ($1, $2);", TableName())
|
||||
}
|
||||
|
||||
func (pg PostgresDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (pg PostgresDialect) deleteVersionSQL() string {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE version_id=$1;", TableName())
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// MySQL
|
||||
////////////////////////////
|
||||
|
||||
// MySQLDialect struct.
|
||||
type MySQLDialect struct{}
|
||||
|
||||
func (m MySQLDialect) createVersionTableSQL() string {
|
||||
return fmt.Sprintf(`CREATE TABLE %s (
|
||||
id serial NOT NULL,
|
||||
version_id bigint NOT NULL,
|
||||
is_applied boolean NOT NULL,
|
||||
tstamp timestamp NULL default now(),
|
||||
PRIMARY KEY(id)
|
||||
);`, TableName())
|
||||
}
|
||||
|
||||
func (m MySQLDialect) insertVersionSQL() string {
|
||||
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||
}
|
||||
|
||||
func (m MySQLDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (m MySQLDialect) deleteVersionSQL() string {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// sqlite3
|
||||
////////////////////////////
|
||||
|
||||
// Sqlite3Dialect struct.
|
||||
type Sqlite3Dialect struct{}
|
||||
|
||||
func (m Sqlite3Dialect) createVersionTableSQL() string {
|
||||
return fmt.Sprintf(`CREATE TABLE %s (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
version_id INTEGER NOT NULL,
|
||||
is_applied INTEGER NOT NULL,
|
||||
tstamp TIMESTAMP DEFAULT (datetime('now'))
|
||||
);`, TableName())
|
||||
}
|
||||
|
||||
func (m Sqlite3Dialect) insertVersionSQL() string {
|
||||
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||
}
|
||||
|
||||
func (m Sqlite3Dialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (m Sqlite3Dialect) deleteVersionSQL() string {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// Redshift
|
||||
////////////////////////////
|
||||
|
||||
// RedshiftDialect struct.
|
||||
type RedshiftDialect struct{}
|
||||
|
||||
func (rs RedshiftDialect) createVersionTableSQL() string {
|
||||
return fmt.Sprintf(`CREATE TABLE %s (
|
||||
id integer NOT NULL identity(1, 1),
|
||||
version_id bigint NOT NULL,
|
||||
is_applied boolean NOT NULL,
|
||||
tstamp timestamp NULL default sysdate,
|
||||
PRIMARY KEY(id)
|
||||
);`, TableName())
|
||||
}
|
||||
|
||||
func (rs RedshiftDialect) insertVersionSQL() string {
|
||||
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES ($1, $2);", TableName())
|
||||
}
|
||||
|
||||
func (rs RedshiftDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (rs RedshiftDialect) deleteVersionSQL() string {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// TiDB
|
||||
////////////////////////////
|
||||
|
||||
// TiDBDialect struct.
|
||||
type TiDBDialect struct{}
|
||||
|
||||
func (m TiDBDialect) createVersionTableSQL() string {
|
||||
return fmt.Sprintf(`CREATE TABLE %s (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
version_id bigint NOT NULL,
|
||||
is_applied boolean NOT NULL,
|
||||
tstamp timestamp NULL default now(),
|
||||
PRIMARY KEY(id)
|
||||
);`, TableName())
|
||||
}
|
||||
|
||||
func (m TiDBDialect) insertVersionSQL() string {
|
||||
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||
}
|
||||
|
||||
func (m TiDBDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (m TiDBDialect) deleteVersionSQL() string {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||
}
|
56
vendor/github.com/pressly/goose/down.go
generated
vendored
Normal file
56
vendor/github.com/pressly/goose/down.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Down rolls back a single migration from the current version.
|
||||
func Down(db *sql.DB, dir string) error {
|
||||
currentVersion, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current, err := migrations.Current(currentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("no migration %v", currentVersion)
|
||||
}
|
||||
|
||||
return current.Down(db)
|
||||
}
|
||||
|
||||
// DownTo rolls back migrations to a specific version.
|
||||
func DownTo(db *sql.DB, dir string, version int64) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
currentVersion, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current, err := migrations.Current(currentVersion)
|
||||
if err != nil {
|
||||
log.Printf("goose: no migrations to run. current version: %d\n", currentVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
if current.Version <= version {
|
||||
log.Printf("goose: no migrations to run. current version: %d\n", currentVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = current.Down(db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
46
vendor/github.com/pressly/goose/fix.go
generated
vendored
Normal file
46
vendor/github.com/pressly/goose/fix.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Fix(dir string) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// split into timestamped and versioned migrations
|
||||
tsMigrations, err := migrations.timestamped()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vMigrations, err := migrations.versioned()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Initial version.
|
||||
version := int64(1)
|
||||
if last, err := vMigrations.Last(); err == nil {
|
||||
version = last.Version + 1
|
||||
}
|
||||
|
||||
// fix filenames by replacing timestamps with sequential versions
|
||||
for _, tsm := range tsMigrations {
|
||||
oldPath := tsm.Source
|
||||
newPath := strings.Replace(oldPath, fmt.Sprintf("%d", tsm.Version), fmt.Sprintf("%05v", version), 1)
|
||||
|
||||
if err := os.Rename(oldPath, newPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("RENAMED %s => %s", filepath.Base(oldPath), filepath.Base(newPath))
|
||||
version++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
100
vendor/github.com/pressly/goose/goose.go
generated
vendored
Normal file
100
vendor/github.com/pressly/goose/goose.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const VERSION = "v2.6.0"
|
||||
|
||||
var (
|
||||
duplicateCheckOnce sync.Once
|
||||
minVersion = int64(0)
|
||||
maxVersion = int64((1 << 63) - 1)
|
||||
timestampFormat = "20060102150405"
|
||||
verbose = false
|
||||
)
|
||||
|
||||
// SetVerbose set the goose verbosity mode
|
||||
func SetVerbose(v bool) {
|
||||
verbose = v
|
||||
}
|
||||
|
||||
// Run runs a goose command.
|
||||
func Run(command string, db *sql.DB, dir string, args ...string) error {
|
||||
switch command {
|
||||
case "up":
|
||||
if err := Up(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "up-by-one":
|
||||
if err := UpByOne(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "up-to":
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("up-to must be of form: goose [OPTIONS] DRIVER DBSTRING up-to VERSION")
|
||||
}
|
||||
|
||||
version, err := strconv.ParseInt(args[0], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("version must be a number (got '%s')", args[0])
|
||||
}
|
||||
if err := UpTo(db, dir, version); err != nil {
|
||||
return err
|
||||
}
|
||||
case "create":
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("create must be of form: goose [OPTIONS] DRIVER DBSTRING create NAME [go|sql]")
|
||||
}
|
||||
|
||||
migrationType := "go"
|
||||
if len(args) == 2 {
|
||||
migrationType = args[1]
|
||||
}
|
||||
if err := Create(db, dir, args[0], migrationType); err != nil {
|
||||
return err
|
||||
}
|
||||
case "down":
|
||||
if err := Down(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "down-to":
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("down-to must be of form: goose [OPTIONS] DRIVER DBSTRING down-to VERSION")
|
||||
}
|
||||
|
||||
version, err := strconv.ParseInt(args[0], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("version must be a number (got '%s')", args[0])
|
||||
}
|
||||
if err := DownTo(db, dir, version); err != nil {
|
||||
return err
|
||||
}
|
||||
case "fix":
|
||||
if err := Fix(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "redo":
|
||||
if err := Redo(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "reset":
|
||||
if err := Reset(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "status":
|
||||
if err := Status(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "version":
|
||||
if err := Version(db, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%q: no such command", command)
|
||||
}
|
||||
return nil
|
||||
}
|
30
vendor/github.com/pressly/goose/log.go
generated
vendored
Normal file
30
vendor/github.com/pressly/goose/log.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
std "log"
|
||||
)
|
||||
|
||||
var log Logger = &stdLogger{}
|
||||
|
||||
// Logger is standart logger interface
|
||||
type Logger interface {
|
||||
Fatal(v ...interface{})
|
||||
Fatalf(format string, v ...interface{})
|
||||
Print(v ...interface{})
|
||||
Println(v ...interface{})
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// SetLogger sets the logger for package output
|
||||
func SetLogger(l Logger) {
|
||||
log = l
|
||||
}
|
||||
|
||||
// stdLogger is a default logger that outputs to a stdlib's log.std logger.
|
||||
type stdLogger struct{}
|
||||
|
||||
func (*stdLogger) Fatal(v ...interface{}) { std.Fatal(v...) }
|
||||
func (*stdLogger) Fatalf(format string, v ...interface{}) { std.Fatalf(format, v...) }
|
||||
func (*stdLogger) Print(v ...interface{}) { std.Print(v...) }
|
||||
func (*stdLogger) Println(v ...interface{}) { std.Println(v...) }
|
||||
func (*stdLogger) Printf(format string, v ...interface{}) { std.Printf(format, v...) }
|
319
vendor/github.com/pressly/goose/migrate.go
generated
vendored
Normal file
319
vendor/github.com/pressly/goose/migrate.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoCurrentVersion when a current migration version is not found.
|
||||
ErrNoCurrentVersion = errors.New("no current version found")
|
||||
// ErrNoNextVersion when the next migration version is not found.
|
||||
ErrNoNextVersion = errors.New("no next version found")
|
||||
// MaxVersion is the maximum allowed version.
|
||||
MaxVersion int64 = 9223372036854775807 // max(int64)
|
||||
|
||||
registeredGoMigrations = map[int64]*Migration{}
|
||||
)
|
||||
|
||||
// Migrations slice.
|
||||
type Migrations []*Migration
|
||||
|
||||
// helpers so we can use pkg sort
|
||||
func (ms Migrations) Len() int { return len(ms) }
|
||||
func (ms Migrations) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
|
||||
func (ms Migrations) Less(i, j int) bool {
|
||||
if ms[i].Version == ms[j].Version {
|
||||
panic(fmt.Sprintf("goose: duplicate version %v detected:\n%v\n%v", ms[i].Version, ms[i].Source, ms[j].Source))
|
||||
}
|
||||
return ms[i].Version < ms[j].Version
|
||||
}
|
||||
|
||||
// Current gets the current migration.
|
||||
func (ms Migrations) Current(current int64) (*Migration, error) {
|
||||
for i, migration := range ms {
|
||||
if migration.Version == current {
|
||||
return ms[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNoCurrentVersion
|
||||
}
|
||||
|
||||
// Next gets the next migration.
|
||||
func (ms Migrations) Next(current int64) (*Migration, error) {
|
||||
for i, migration := range ms {
|
||||
if migration.Version > current {
|
||||
return ms[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNoNextVersion
|
||||
}
|
||||
|
||||
// Previous : Get the previous migration.
|
||||
func (ms Migrations) Previous(current int64) (*Migration, error) {
|
||||
for i := len(ms) - 1; i >= 0; i-- {
|
||||
if ms[i].Version < current {
|
||||
return ms[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNoNextVersion
|
||||
}
|
||||
|
||||
// Last gets the last migration.
|
||||
func (ms Migrations) Last() (*Migration, error) {
|
||||
if len(ms) == 0 {
|
||||
return nil, ErrNoNextVersion
|
||||
}
|
||||
|
||||
return ms[len(ms)-1], nil
|
||||
}
|
||||
|
||||
// Versioned gets versioned migrations.
|
||||
func (ms Migrations) versioned() (Migrations, error) {
|
||||
var migrations Migrations
|
||||
|
||||
// assume that the user will never have more than 19700101000000 migrations
|
||||
for _, m := range ms {
|
||||
// parse version as timestmap
|
||||
versionTime, err := time.Parse(timestampFormat, fmt.Sprintf("%d", m.Version))
|
||||
|
||||
if versionTime.Before(time.Unix(0, 0)) || err != nil {
|
||||
migrations = append(migrations, m)
|
||||
}
|
||||
}
|
||||
|
||||
return migrations, nil
|
||||
}
|
||||
|
||||
// Timestamped gets the timestamped migrations.
|
||||
func (ms Migrations) timestamped() (Migrations, error) {
|
||||
var migrations Migrations
|
||||
|
||||
// assume that the user will never have more than 19700101000000 migrations
|
||||
for _, m := range ms {
|
||||
// parse version as timestmap
|
||||
versionTime, err := time.Parse(timestampFormat, fmt.Sprintf("%d", m.Version))
|
||||
if err != nil {
|
||||
// probably not a timestamp
|
||||
continue
|
||||
}
|
||||
|
||||
if versionTime.After(time.Unix(0, 0)) {
|
||||
migrations = append(migrations, m)
|
||||
}
|
||||
}
|
||||
return migrations, nil
|
||||
}
|
||||
|
||||
func (ms Migrations) String() string {
|
||||
str := ""
|
||||
for _, m := range ms {
|
||||
str += fmt.Sprintln(m)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// AddMigration adds a migration.
|
||||
func AddMigration(up func(*sql.Tx) error, down func(*sql.Tx) error) {
|
||||
_, filename, _, _ := runtime.Caller(1)
|
||||
AddNamedMigration(filename, up, down)
|
||||
}
|
||||
|
||||
// AddNamedMigration : Add a named migration.
|
||||
func AddNamedMigration(filename string, up func(*sql.Tx) error, down func(*sql.Tx) error) {
|
||||
v, _ := NumericComponent(filename)
|
||||
migration := &Migration{Version: v, Next: -1, Previous: -1, Registered: true, UpFn: up, DownFn: down, Source: filename}
|
||||
|
||||
if existing, ok := registeredGoMigrations[v]; ok {
|
||||
panic(fmt.Sprintf("failed to add migration %q: version conflicts with %q", filename, existing.Source))
|
||||
}
|
||||
|
||||
registeredGoMigrations[v] = migration
|
||||
}
|
||||
|
||||
// CollectMigrations returns all the valid looking migration scripts in the
|
||||
// migrations folder and go func registry, and key them by version.
|
||||
func CollectMigrations(dirpath string, current, target int64) (Migrations, error) {
|
||||
if _, err := os.Stat(dirpath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("%s directory does not exists", dirpath)
|
||||
}
|
||||
|
||||
var migrations Migrations
|
||||
|
||||
// SQL migration files.
|
||||
sqlMigrationFiles, err := filepath.Glob(dirpath + "/**.sql")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range sqlMigrationFiles {
|
||||
v, err := NumericComponent(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if versionFilter(v, current, target) {
|
||||
migration := &Migration{Version: v, Next: -1, Previous: -1, Source: file}
|
||||
migrations = append(migrations, migration)
|
||||
}
|
||||
}
|
||||
|
||||
// Go migrations registered via goose.AddMigration().
|
||||
for _, migration := range registeredGoMigrations {
|
||||
v, err := NumericComponent(migration.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if versionFilter(v, current, target) {
|
||||
migrations = append(migrations, migration)
|
||||
}
|
||||
}
|
||||
|
||||
// Go migration files
|
||||
goMigrationFiles, err := filepath.Glob(dirpath + "/**.go")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range goMigrationFiles {
|
||||
v, err := NumericComponent(file)
|
||||
if err != nil {
|
||||
continue // Skip any files that don't have version prefix.
|
||||
}
|
||||
|
||||
// Skip migrations already existing migrations registered via goose.AddMigration().
|
||||
if _, ok := registeredGoMigrations[v]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if versionFilter(v, current, target) {
|
||||
migration := &Migration{Version: v, Next: -1, Previous: -1, Source: file, Registered: false}
|
||||
migrations = append(migrations, migration)
|
||||
}
|
||||
}
|
||||
|
||||
migrations = sortAndConnectMigrations(migrations)
|
||||
|
||||
return migrations, nil
|
||||
}
|
||||
|
||||
func sortAndConnectMigrations(migrations Migrations) Migrations {
|
||||
sort.Sort(migrations)
|
||||
|
||||
// now that we're sorted in the appropriate direction,
|
||||
// populate next and previous for each migration
|
||||
for i, m := range migrations {
|
||||
prev := int64(-1)
|
||||
if i > 0 {
|
||||
prev = migrations[i-1].Version
|
||||
migrations[i-1].Next = m.Version
|
||||
}
|
||||
migrations[i].Previous = prev
|
||||
}
|
||||
|
||||
return migrations
|
||||
}
|
||||
|
||||
func versionFilter(v, current, target int64) bool {
|
||||
|
||||
if target > current {
|
||||
return v > current && v <= target
|
||||
}
|
||||
|
||||
if target < current {
|
||||
return v <= current && v > target
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// EnsureDBVersion retrieves the current version for this DB.
|
||||
// Create and initialize the DB version table if it doesn't exist.
|
||||
func EnsureDBVersion(db *sql.DB) (int64, error) {
|
||||
rows, err := GetDialect().dbVersionQuery(db)
|
||||
if err != nil {
|
||||
return 0, createVersionTable(db)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// The most recent record for each migration specifies
|
||||
// whether it has been applied or rolled back.
|
||||
// The first version we find that has been applied is the current version.
|
||||
|
||||
toSkip := make([]int64, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var row MigrationRecord
|
||||
if err = rows.Scan(&row.VersionID, &row.IsApplied); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to scan row")
|
||||
}
|
||||
|
||||
// have we already marked this version to be skipped?
|
||||
skip := false
|
||||
for _, v := range toSkip {
|
||||
if v == row.VersionID {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
// if version has been applied we're done
|
||||
if row.IsApplied {
|
||||
return row.VersionID, nil
|
||||
}
|
||||
|
||||
// latest version of migration has not been applied.
|
||||
toSkip = append(toSkip, row.VersionID)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to get next row")
|
||||
}
|
||||
|
||||
return 0, ErrNoNextVersion
|
||||
}
|
||||
|
||||
// Create the db version table
|
||||
// and insert the initial 0 value into it
|
||||
func createVersionTable(db *sql.DB) error {
|
||||
txn, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := GetDialect()
|
||||
|
||||
if _, err := txn.Exec(d.createVersionTableSQL()); err != nil {
|
||||
txn.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
version := 0
|
||||
applied := true
|
||||
if _, err := txn.Exec(d.insertVersionSQL(), version, applied); err != nil {
|
||||
txn.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return txn.Commit()
|
||||
}
|
||||
|
||||
// GetDBVersion is an alias for EnsureDBVersion, but returns -1 in error.
|
||||
func GetDBVersion(db *sql.DB) (int64, error) {
|
||||
version, err := EnsureDBVersion(db)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
124
vendor/github.com/pressly/goose/migration.go
generated
vendored
Normal file
124
vendor/github.com/pressly/goose/migration.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MigrationRecord struct.
|
||||
type MigrationRecord struct {
|
||||
VersionID int64
|
||||
TStamp time.Time
|
||||
IsApplied bool // was this a result of up() or down()
|
||||
}
|
||||
|
||||
// Migration struct.
|
||||
type Migration struct {
|
||||
Version int64
|
||||
Next int64 // next version, or -1 if none
|
||||
Previous int64 // previous version, -1 if none
|
||||
Source string // path to .sql script
|
||||
Registered bool
|
||||
UpFn func(*sql.Tx) error // Up go migration function
|
||||
DownFn func(*sql.Tx) error // Down go migration function
|
||||
}
|
||||
|
||||
func (m *Migration) String() string {
|
||||
return fmt.Sprintf(m.Source)
|
||||
}
|
||||
|
||||
// Up runs an up migration.
|
||||
func (m *Migration) Up(db *sql.DB) error {
|
||||
if err := m.run(db, true); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("OK ", filepath.Base(m.Source))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Down runs a down migration.
|
||||
func (m *Migration) Down(db *sql.DB) error {
|
||||
if err := m.run(db, false); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("OK ", filepath.Base(m.Source))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migration) run(db *sql.DB, direction bool) error {
|
||||
switch filepath.Ext(m.Source) {
|
||||
case ".sql":
|
||||
if err := runSQLMigration(db, m.Source, m.Version, direction); err != nil {
|
||||
return errors.Wrapf(err, "failed to run SQL migration %q", filepath.Base(m.Source))
|
||||
}
|
||||
|
||||
case ".go":
|
||||
if !m.Registered {
|
||||
return errors.Errorf("failed to run Go migration %q: Go functions must be registered and built into a custom binary (see https://github.com/pressly/goose/tree/master/examples/go-migrations)", m.Source)
|
||||
}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
|
||||
fn := m.UpFn
|
||||
if !direction {
|
||||
fn = m.DownFn
|
||||
}
|
||||
if fn != nil {
|
||||
if err := fn(tx); err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrapf(err, "failed to run Go migration %q", filepath.Base(m.Source))
|
||||
}
|
||||
}
|
||||
|
||||
if direction {
|
||||
if _, err := tx.Exec(GetDialect().insertVersionSQL(), m.Version, direction); err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to execute transaction")
|
||||
}
|
||||
} else {
|
||||
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), m.Version); err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to execute transaction")
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NumericComponent looks for migration scripts with names in the form:
|
||||
// XXX_descriptivename.ext where XXX specifies the version number
|
||||
// and ext specifies the type of migration
|
||||
func NumericComponent(name string) (int64, error) {
|
||||
base := filepath.Base(name)
|
||||
|
||||
if ext := filepath.Ext(base); ext != ".go" && ext != ".sql" {
|
||||
return 0, errors.New("not a recognized migration file type")
|
||||
}
|
||||
|
||||
idx := strings.Index(base, "_")
|
||||
if idx < 0 {
|
||||
return 0, errors.New("no separator found")
|
||||
}
|
||||
|
||||
n, e := strconv.ParseInt(base[:idx], 10, 64)
|
||||
if e == nil && n <= 0 {
|
||||
return 0, errors.New("migration IDs must be greater than zero")
|
||||
}
|
||||
|
||||
return n, e
|
||||
}
|
240
vendor/github.com/pressly/goose/migration_sql.go
generated
vendored
Normal file
240
vendor/github.com/pressly/goose/migration_sql.go
generated
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const sqlCmdPrefix = "-- +goose "
|
||||
const scanBufSize = 4 * 1024 * 1024
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, scanBufSize)
|
||||
},
|
||||
}
|
||||
|
||||
// Checks the line to see if the line has a statement-ending semicolon
|
||||
// or if the line contains a double-dash comment.
|
||||
func endsWithSemicolon(line string) bool {
|
||||
scanBuf := bufferPool.Get().([]byte)
|
||||
defer bufferPool.Put(scanBuf)
|
||||
|
||||
prev := ""
|
||||
scanner := bufio.NewScanner(strings.NewReader(line))
|
||||
scanner.Buffer(scanBuf, scanBufSize)
|
||||
scanner.Split(bufio.ScanWords)
|
||||
|
||||
for scanner.Scan() {
|
||||
word := scanner.Text()
|
||||
if strings.HasPrefix(word, "--") {
|
||||
break
|
||||
}
|
||||
prev = word
|
||||
}
|
||||
|
||||
return strings.HasSuffix(prev, ";")
|
||||
}
|
||||
|
||||
// Split the given sql script into individual statements.
|
||||
//
|
||||
// The base case is to simply split on semicolons, as these
|
||||
// naturally terminate a statement.
|
||||
//
|
||||
// However, more complex cases like pl/pgsql can have semicolons
|
||||
// within a statement. For these cases, we provide the explicit annotations
|
||||
// 'StatementBegin' and 'StatementEnd' to allow the script to
|
||||
// tell us to ignore semicolons.
|
||||
func getSQLStatements(r io.Reader, direction bool) ([]string, bool, error) {
|
||||
var buf bytes.Buffer
|
||||
scanBuf := bufferPool.Get().([]byte)
|
||||
defer bufferPool.Put(scanBuf)
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Buffer(scanBuf, scanBufSize)
|
||||
|
||||
// track the count of each section
|
||||
// so we can diagnose scripts with no annotations
|
||||
upSections := 0
|
||||
downSections := 0
|
||||
|
||||
statementEnded := false
|
||||
ignoreSemicolons := false
|
||||
directionIsActive := false
|
||||
tx := true
|
||||
stmts := []string{}
|
||||
|
||||
for scanner.Scan() {
|
||||
|
||||
line := scanner.Text()
|
||||
|
||||
// handle any goose-specific commands
|
||||
if strings.HasPrefix(line, sqlCmdPrefix) {
|
||||
cmd := strings.TrimSpace(line[len(sqlCmdPrefix):])
|
||||
switch cmd {
|
||||
case "Up":
|
||||
directionIsActive = (direction == true)
|
||||
upSections++
|
||||
break
|
||||
|
||||
case "Down":
|
||||
directionIsActive = (direction == false)
|
||||
downSections++
|
||||
break
|
||||
|
||||
case "StatementBegin":
|
||||
if directionIsActive {
|
||||
ignoreSemicolons = true
|
||||
}
|
||||
break
|
||||
|
||||
case "StatementEnd":
|
||||
if directionIsActive {
|
||||
statementEnded = (ignoreSemicolons == true)
|
||||
ignoreSemicolons = false
|
||||
}
|
||||
break
|
||||
|
||||
case "NO TRANSACTION":
|
||||
tx = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !directionIsActive {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := buf.WriteString(line + "\n"); err != nil {
|
||||
return nil, false, fmt.Errorf("io err: %v", err)
|
||||
}
|
||||
|
||||
// Wrap up the two supported cases: 1) basic with semicolon; 2) psql statement
|
||||
// Lines that end with semicolon that are in a statement block
|
||||
// do not conclude statement.
|
||||
if (!ignoreSemicolons && endsWithSemicolon(line)) || statementEnded {
|
||||
statementEnded = false
|
||||
stmts = append(stmts, buf.String())
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, false, fmt.Errorf("scanning migration: %v", err)
|
||||
}
|
||||
|
||||
// diagnose likely migration script errors
|
||||
if ignoreSemicolons {
|
||||
return nil, false, fmt.Errorf("parsing migration: saw '-- +goose StatementBegin' with no matching '-- +goose StatementEnd'")
|
||||
}
|
||||
|
||||
if bufferRemaining := strings.TrimSpace(buf.String()); len(bufferRemaining) > 0 {
|
||||
return nil, false, fmt.Errorf("parsing migration: unexpected unfinished SQL query: %s. potential missing semicolon", bufferRemaining)
|
||||
}
|
||||
|
||||
if upSections == 0 && downSections == 0 {
|
||||
return nil, false, fmt.Errorf("parsing migration: no Up/Down annotations found, so no statements were executed. See https://bitbucket.org/liamstask/goose/overview for details")
|
||||
}
|
||||
|
||||
return stmts, tx, nil
|
||||
}
|
||||
|
||||
// Run a migration specified in raw SQL.
|
||||
//
|
||||
// Sections of the script can be annotated with a special comment,
|
||||
// starting with "-- +goose" to specify whether the section should
|
||||
// be applied during an Up or Down migration
|
||||
//
|
||||
// All statements following an Up or Down directive are grouped together
|
||||
// until another direction directive is found.
|
||||
func runSQLMigration(db *sql.DB, sqlFile string, v int64, direction bool) error {
|
||||
f, err := os.Open(sqlFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open SQL migration file")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
statements, useTx, err := getSQLStatements(f, direction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if useTx {
|
||||
// TRANSACTION.
|
||||
|
||||
printInfo("Begin transaction\n")
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
|
||||
for _, query := range statements {
|
||||
printInfo("Executing statement: %s\n", clearStatement(query))
|
||||
if _, err = tx.Exec(query); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||
}
|
||||
}
|
||||
|
||||
if direction {
|
||||
if _, err := tx.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to insert new goose version")
|
||||
}
|
||||
} else {
|
||||
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), v); err != nil {
|
||||
printInfo("Rollback transaction\n")
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to delete goose version")
|
||||
}
|
||||
}
|
||||
|
||||
printInfo("Commit transaction\n")
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NO TRANSACTION.
|
||||
for _, query := range statements {
|
||||
printInfo("Executing statement: %s\n", clearStatement(query))
|
||||
if _, err := db.Exec(query); err != nil {
|
||||
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||
}
|
||||
}
|
||||
if _, err := db.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||
return errors.Wrap(err, "failed to insert new goose version")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printInfo(s string, args ...interface{}) {
|
||||
if verbose {
|
||||
log.Printf(s, args...)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
matchSQLComments = regexp.MustCompile(`(?m)^--.*$[\r\n]*`)
|
||||
matchEmptyLines = regexp.MustCompile(`(?m)^$[\r\n]*`)
|
||||
)
|
||||
|
||||
func clearStatement(s string) string {
|
||||
s = matchSQLComments.ReplaceAllString(s, ``)
|
||||
return matchEmptyLines.ReplaceAllString(s, ``)
|
||||
}
|
33
vendor/github.com/pressly/goose/redo.go
generated
vendored
Normal file
33
vendor/github.com/pressly/goose/redo.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// Redo rolls back the most recently applied migration, then runs it again.
|
||||
func Redo(db *sql.DB, dir string) error {
|
||||
currentVersion, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current, err := migrations.Current(currentVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := current.Down(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := current.Up(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
60
vendor/github.com/pressly/goose/reset.go
generated
vendored
Normal file
60
vendor/github.com/pressly/goose/reset.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Reset rolls back all migrations
|
||||
func Reset(db *sql.DB, dir string) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to collect migrations")
|
||||
}
|
||||
statuses, err := dbMigrationsStatus(db)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get status of migrations")
|
||||
}
|
||||
sort.Sort(sort.Reverse(migrations))
|
||||
|
||||
for _, migration := range migrations {
|
||||
if !statuses[migration.Version] {
|
||||
continue
|
||||
}
|
||||
if err = migration.Down(db); err != nil {
|
||||
return errors.Wrap(err, "failed to db-down")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dbMigrationsStatus(db *sql.DB) (map[int64]bool, error) {
|
||||
rows, err := GetDialect().dbVersionQuery(db)
|
||||
if err != nil {
|
||||
return map[int64]bool{}, nil
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// The most recent record for each migration specifies
|
||||
// whether it has been applied or rolled back.
|
||||
|
||||
result := make(map[int64]bool)
|
||||
|
||||
for rows.Next() {
|
||||
var row MigrationRecord
|
||||
if err = rows.Scan(&row.VersionID, &row.IsApplied); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to scan row")
|
||||
}
|
||||
|
||||
if _, ok := result[row.VersionID]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
result[row.VersionID] = row.IsApplied
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
54
vendor/github.com/pressly/goose/status.go
generated
vendored
Normal file
54
vendor/github.com/pressly/goose/status.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Status prints the status of all migrations.
|
||||
func Status(db *sql.DB, dir string) error {
|
||||
// collect all migrations
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to collect migrations")
|
||||
}
|
||||
|
||||
// must ensure that the version table exists if we're running on a pristine DB
|
||||
if _, err := EnsureDBVersion(db); err != nil {
|
||||
return errors.Wrap(err, "failed to ensure DB version")
|
||||
}
|
||||
|
||||
log.Println(" Applied At Migration")
|
||||
log.Println(" =======================================")
|
||||
for _, migration := range migrations {
|
||||
if err := printMigrationStatus(db, migration.Version, filepath.Base(migration.Source)); err != nil {
|
||||
return errors.Wrap(err, "failed to print status")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMigrationStatus(db *sql.DB, version int64, script string) error {
|
||||
q := fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=%d ORDER BY tstamp DESC LIMIT 1", TableName(), version)
|
||||
|
||||
var row MigrationRecord
|
||||
err := db.QueryRow(q).Scan(&row.TStamp, &row.IsApplied)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "failed to query the latest migration")
|
||||
}
|
||||
|
||||
var appliedAt string
|
||||
if row.IsApplied {
|
||||
appliedAt = row.TStamp.Format(time.ANSIC)
|
||||
} else {
|
||||
appliedAt = "Pending"
|
||||
}
|
||||
|
||||
log.Printf(" %-24s -- %v\n", appliedAt, script)
|
||||
return nil
|
||||
}
|
65
vendor/github.com/pressly/goose/up.go
generated
vendored
Normal file
65
vendor/github.com/pressly/goose/up.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// UpTo migrates up to a specific version.
|
||||
func UpTo(db *sql.DB, dir string, version int64) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
current, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
next, err := migrations.Next(current)
|
||||
if err != nil {
|
||||
if err == ErrNoNextVersion {
|
||||
log.Printf("goose: no migrations to run. current version: %d\n", current)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err = next.Up(db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Up applies all available migrations.
|
||||
func Up(db *sql.DB, dir string) error {
|
||||
return UpTo(db, dir, maxVersion)
|
||||
}
|
||||
|
||||
// UpByOne migrates up by a single version.
|
||||
func UpByOne(db *sql.DB, dir string) error {
|
||||
migrations, err := CollectMigrations(dir, minVersion, maxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentVersion, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
next, err := migrations.Next(currentVersion)
|
||||
if err != nil {
|
||||
if err == ErrNoNextVersion {
|
||||
log.Printf("goose: no migrations to run. current version: %d\n", currentVersion)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err = next.Up(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
28
vendor/github.com/pressly/goose/version.go
generated
vendored
Normal file
28
vendor/github.com/pressly/goose/version.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// Version prints the current version of the database.
|
||||
func Version(db *sql.DB, dir string) error {
|
||||
current, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("goose: version %v\n", current)
|
||||
return nil
|
||||
}
|
||||
|
||||
var tableName = "goose_db_version"
|
||||
|
||||
// TableName returns goose db version table name
|
||||
func TableName() string {
|
||||
return tableName
|
||||
}
|
||||
|
||||
// SetTableName set goose db version table name
|
||||
func SetTableName(n string) {
|
||||
tableName = n
|
||||
}
|
Reference in New Issue
Block a user