Replace go-migrate with morph (#19116)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c8198fbefe
Коммит
6595b31f23
1
vendor/github.com/go-morph/morph/.gitignore
сгенерированный
поставляемый
Обычный файл
1
vendor/github.com/go-morph/morph/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1 @@
|
||||
.idea
|
||||
12
vendor/github.com/go-morph/morph/AUTHORS
сгенерированный
поставляемый
Обычный файл
12
vendor/github.com/go-morph/morph/AUTHORS
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,12 @@
|
||||
# This is the official list of go-morph authors for copyright purposes.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code, since in
|
||||
# some cases, their employer may be the copyright holder. To see the full list
|
||||
# of contributors, see the revision history in source control or
|
||||
# https://github.com/go-morph/morph/graphs/contributors.
|
||||
#
|
||||
# Authors who wish to be recognized in this file should add themselves (or
|
||||
# their employer, as appropriate).
|
||||
|
||||
mgdelacroix
|
||||
nronas
|
||||
22
vendor/github.com/go-morph/morph/LICENSE
сгенерированный
поставляемый
Обычный файл
22
vendor/github.com/go-morph/morph/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 The go-morph AUTHORS. All rights reserved.
|
||||
https://github.com/go-morph/morph
|
||||
|
||||
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.
|
||||
42
vendor/github.com/go-morph/morph/Makefile
сгенерированный
поставляемый
Обычный файл
42
vendor/github.com/go-morph/morph/Makefile
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
all: test
|
||||
|
||||
GO=go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GO) clean -testcache
|
||||
make test-drivers
|
||||
make test-rest
|
||||
|
||||
.PHONY: test-rest
|
||||
test-rest:
|
||||
$(GO) clean -testcache
|
||||
$(GO) test -race -v --tags=!drivers,sources ./...
|
||||
|
||||
.PHONY: test-drivers
|
||||
test-drivers:
|
||||
$(GO) clean -testcache
|
||||
$(GO) test -race -v --tags=drivers,!sources ./...
|
||||
|
||||
.PHONY: update-dependencies
|
||||
update-dependencies:
|
||||
$(GO) get -u ./...
|
||||
$(GO) mod vendor
|
||||
$(GO) mod tidy
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
$(GO) mod vendor
|
||||
$(GO) mod tidy
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
$(GO) fmt ./...
|
||||
|
||||
.PHONY: run-databases
|
||||
run-databases:
|
||||
docker-compose up --no-recreate -d
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
$(GO) install -mod=readonly -trimpath ./cmd/morph
|
||||
78
vendor/github.com/go-morph/morph/README.md
сгенерированный
поставляемый
Обычный файл
78
vendor/github.com/go-morph/morph/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,78 @@
|
||||

|
||||
|
||||
|
||||
[](https://github.com/go-morph/morph/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
[](https://pkg.go.dev/github.com/go-morph/morph)
|
||||
|
||||
# Morph
|
||||
|
||||
Morph is a database migration tool that helps you to apply your migrations. It is written with Go so you can use it from your Go application as well.
|
||||
|
||||
## Usage
|
||||
|
||||
It can be used as a library or a CLI tool.
|
||||
|
||||
### Library
|
||||
|
||||
```Go
|
||||
import (
|
||||
"github.com/go-morph/morph"
|
||||
"github.com/go-morph/morph/drivers/mysql"
|
||||
bindata "github.com/go-morph/morph/sources/go_bindata"
|
||||
)
|
||||
|
||||
src, err := bindata.WithInstance(&bindata.AssetSource{
|
||||
Names: []string{}, // add migration file names
|
||||
AssetFunc: func(name string) ([]byte, error) {
|
||||
return []byte{}, nil // should return the file contents
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
driver, err := mysql.WithInstance(db, &mysql.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
engine, err := morph.New(driver, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
engine.ApplyAll()
|
||||
|
||||
```
|
||||
|
||||
### CLI
|
||||
|
||||
To install `morph` you can use:
|
||||
|
||||
```bash
|
||||
go install github.com/go-morph/morph/cmd/morph@latest
|
||||
```
|
||||
|
||||
Then you can apply your migrations like below:
|
||||
|
||||
```bash
|
||||
morph apply up --driver postgres --dsn "postgres://user:pass@localhost:5432/mydb?sslmode=disable" --path ./db/migrations/postgres --number 1
|
||||
```
|
||||
|
||||
## Migration Files
|
||||
|
||||
The migrations files should have an `up` and `down` versions. The program requires each migration to be reversible, and the naming of the migration should be in the following form:
|
||||
```
|
||||
0000000001_create_user.up.sql
|
||||
0000000001_create_user.down.sql
|
||||
```
|
||||
|
||||
The first part will be used to determine the order in which the migrations should be applied and the next part until the `up|down.sql` suffix will be the migration name.
|
||||
|
||||
The program requires this naming convention to be followed as it saves the order and names of the migrations. Also, it can rollback migrations with the `down` files.
|
||||
|
||||
## LICENSE
|
||||
|
||||
[MIT](LICENSE)
|
||||
18
vendor/github.com/go-morph/morph/color_logger.go
сгенерированный
поставляемый
Обычный файл
18
vendor/github.com/go-morph/morph/color_logger.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,18 @@
|
||||
package morph
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorLogger = color.New(color.FgRed, color.Bold)
|
||||
ErrorLoggerLight = color.New(color.FgRed)
|
||||
InfoLogger = color.New(color.FgCyan, color.Bold)
|
||||
InfoLoggerLight = color.New(color.FgCyan)
|
||||
SuccessLogger = color.New(color.FgGreen, color.Bold)
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Printf(format string, v ...interface{})
|
||||
Println(v ...interface{})
|
||||
}
|
||||
20
vendor/github.com/go-morph/morph/docker-compose.yml
сгенерированный
поставляемый
Обычный файл
20
vendor/github.com/go-morph/morph/docker-compose.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,20 @@
|
||||
version: '3.1'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
restart: always
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_PASSWORD: morph
|
||||
mysql:
|
||||
image: "mysql:5.7"
|
||||
restart: always
|
||||
ports:
|
||||
- "3307:3306"
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
environment:
|
||||
MYSQL_DATABASE: morph_test
|
||||
MYSQL_USER: morph
|
||||
MYSQL_PASSWORD: morph
|
||||
MYSQL_ROOT_PASSWORD: morph
|
||||
17
vendor/github.com/go-morph/morph/drivers/driver.go
сгенерированный
поставляемый
Обычный файл
17
vendor/github.com/go-morph/morph/drivers/driver.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,17 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"github.com/go-morph/morph/models"
|
||||
)
|
||||
|
||||
type Driver interface {
|
||||
Ping() error
|
||||
// Close closes the underlying db connection. If the driver is created via Open() function
|
||||
// this method will also going to call Close() on the sql.db instance.
|
||||
Close() error
|
||||
Lock() error
|
||||
Unlock() error
|
||||
Apply(migration *models.Migration, saveVersion bool) error
|
||||
AppliedMigrations() ([]*models.Migration, error)
|
||||
SetConfig(key string, value interface{}) error
|
||||
}
|
||||
25
vendor/github.com/go-morph/morph/drivers/error.go
сгенерированный
поставляемый
Обычный файл
25
vendor/github.com/go-morph/morph/drivers/error.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,25 @@
|
||||
package drivers
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AppError struct {
|
||||
OrigErr error
|
||||
Driver string
|
||||
Message string
|
||||
}
|
||||
|
||||
type DatabaseError struct {
|
||||
OrigErr error
|
||||
Driver string
|
||||
Message string
|
||||
Command string
|
||||
Query []byte
|
||||
}
|
||||
|
||||
func (ae *AppError) Error() string {
|
||||
return fmt.Sprintf("driver: %s, message: %s, originalError: %v ", ae.Driver, ae.Message, ae.OrigErr)
|
||||
}
|
||||
|
||||
func (de *DatabaseError) Error() string {
|
||||
return fmt.Sprintf("driver: %s, message: %s, command: %s, originalError: %v, query: \n\n%s\n", de.Driver, de.Message, de.Command, de.OrigErr, string(de.Query))
|
||||
}
|
||||
418
vendor/github.com/go-morph/morph/drivers/mysql/mysql.go
сгенерированный
поставляемый
Обычный файл
418
vendor/github.com/go-morph/morph/drivers/mysql/mysql.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,418 @@
|
||||
// Initial code generated by generator.
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/go-morph/morph/drivers"
|
||||
"github.com/go-morph/morph/models"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
const driverName = "mysql"
|
||||
const defaultMigrationMaxSize = 10 * 1 << 20 // 10 MB
|
||||
var defaultConfig = &Config{
|
||||
MigrationsTable: "db_migrations",
|
||||
StatementTimeoutInSecs: 60,
|
||||
MigrationMaxSize: defaultMigrationMaxSize,
|
||||
}
|
||||
|
||||
// add here any custom driver configuration
|
||||
var configParams = []string{
|
||||
"x-migration-max-size",
|
||||
"x-migrations-table",
|
||||
"x-statement-timeout",
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
MigrationsTable string
|
||||
StatementTimeoutInSecs int
|
||||
MigrationMaxSize int
|
||||
databaseName string
|
||||
closeDBonClose bool
|
||||
}
|
||||
|
||||
type mysql struct {
|
||||
conn *sql.Conn
|
||||
db *sql.DB
|
||||
config *Config
|
||||
}
|
||||
|
||||
func WithInstance(dbInstance *sql.DB, config *Config) (drivers.Driver, error) {
|
||||
driverConfig := mergeConfigs(config, defaultConfig)
|
||||
|
||||
conn, err := dbInstance.Conn(context.Background())
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "grabbing_connection", OrigErr: err, Message: "failed to grab connection to the database"}
|
||||
}
|
||||
|
||||
if driverConfig.databaseName, err = currentDatabaseNameFromDB(conn, driverConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mysql{config: driverConfig, conn: conn, db: dbInstance}, nil
|
||||
}
|
||||
|
||||
func Open(connURL string) (drivers.Driver, error) {
|
||||
customParams, err := drivers.ExtractCustomParams(connURL, configParams)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to parse custom parameters from url"}
|
||||
}
|
||||
|
||||
sanitizedConnURL, err := drivers.RemoveParamsFromURL(connURL, configParams)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to sanitize url from custom parameters"}
|
||||
}
|
||||
|
||||
driverConfig, err := mergeConfigWithParams(customParams, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to merge custom params to driver config"}
|
||||
}
|
||||
|
||||
db, err := sql.Open(driverName, sanitizedConnURL)
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "opening_connection", OrigErr: err, Message: "failed to open connection with the database"}
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "grabbing_connection", OrigErr: err, Message: "failed to grab connection to the database"}
|
||||
}
|
||||
|
||||
if driverConfig.databaseName, err = extractDatabaseNameFromURL(sanitizedConnURL); err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to extract database name from connection url"}
|
||||
}
|
||||
|
||||
driverConfig.closeDBonClose = true
|
||||
|
||||
return &mysql{
|
||||
conn: conn,
|
||||
db: db,
|
||||
config: driverConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (driver *mysql) Ping() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return driver.conn.PingContext(ctx)
|
||||
}
|
||||
|
||||
func (driver *mysql) Close() error {
|
||||
if driver.conn != nil {
|
||||
if err := driver.conn.Close(); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to close database connection",
|
||||
Command: "mysql_conn_close",
|
||||
Query: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if driver.db != nil && driver.config.closeDBonClose {
|
||||
if err := driver.db.Close(); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to close database",
|
||||
Command: "mysql_db_close",
|
||||
Query: nil,
|
||||
}
|
||||
}
|
||||
driver.db = nil
|
||||
}
|
||||
|
||||
driver.conn = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *mysql) Lock() error {
|
||||
aid, err := drivers.GenerateAdvisoryLockID(driver.config.databaseName, driver.config.MigrationsTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This will wait until the lock can be acquired or until the statement timeout has reached.
|
||||
query := fmt.Sprintf("SELECT GET_LOCK(?, %d)", driver.config.StatementTimeoutInSecs)
|
||||
var success bool
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := driver.conn.QueryRowContext(ctx, query, aid).Scan(&success); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to obtain advisory lock due to error",
|
||||
Command: "lock_migrations_table",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to obtain advisory lock, potentially due to timeout",
|
||||
Command: "lock_migrations_table",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *mysql) Unlock() error {
|
||||
aid, err := drivers.GenerateAdvisoryLockID(driver.config.databaseName, driver.config.MigrationsTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `SELECT RELEASE_LOCK(?)`
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := driver.conn.ExecContext(ctx, query, aid); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to unlock advisory lock",
|
||||
Command: "unlock_migrations_table",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *mysql) createSchemaTableIfNotExists() (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
createTableIfNotExistsQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (Version bigint(20) NOT NULL, Name varchar(64) NOT NULL, PRIMARY KEY (Version)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", driver.config.MigrationsTable)
|
||||
if _, err = driver.conn.ExecContext(ctx, createTableIfNotExistsQuery); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed while executing query",
|
||||
Command: "create_migrations_table_if_not_exists",
|
||||
Query: []byte(createTableIfNotExistsQuery),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *mysql) Apply(migration *models.Migration, saveVersion bool) (err error) {
|
||||
if err = driver.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// If we saw no error prior to unlocking and unlocking returns an error we need to
|
||||
// assign the unlocking error to err
|
||||
if unlockErr := driver.Unlock(); unlockErr != nil && err == nil {
|
||||
err = unlockErr
|
||||
}
|
||||
}()
|
||||
|
||||
query, readErr := migration.Query()
|
||||
if readErr != nil {
|
||||
return &drivers.AppError{
|
||||
OrigErr: readErr,
|
||||
Driver: driverName,
|
||||
Message: fmt.Sprintf("failed to read migration query: %s", migration.Name),
|
||||
}
|
||||
}
|
||||
defer migration.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := driver.conn.ExecContext(ctx, query); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed when applying migration",
|
||||
Command: "apply_migration",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
updateVersionContext, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if !saveVersion {
|
||||
return nil
|
||||
}
|
||||
|
||||
updateVersionQuery := driver.addMigrationQuery(migration)
|
||||
if _, err := driver.conn.ExecContext(updateVersionContext, updateVersionQuery); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed when updating migrations table with the new version",
|
||||
Command: "update_version",
|
||||
Query: []byte(updateVersionQuery),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *mysql) AppliedMigrations() (migrations []*models.Migration, err error) {
|
||||
if driver.conn == nil {
|
||||
return nil, &drivers.AppError{
|
||||
OrigErr: errors.New("driver has no connection established"),
|
||||
Message: "database connection is missing",
|
||||
Driver: driverName,
|
||||
}
|
||||
}
|
||||
|
||||
if err = driver.Lock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
// If we saw no error prior to unlocking and unlocking returns an error we need to
|
||||
// assign the unlocking error to err
|
||||
if unlockErr := driver.Unlock(); unlockErr != nil && err == nil {
|
||||
err = unlockErr
|
||||
}
|
||||
}()
|
||||
|
||||
if err := driver.createSchemaTableIfNotExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT version, name FROM %s", driver.config.MigrationsTable)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(driver.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
var appliedMigrations []*models.Migration
|
||||
var version uint32
|
||||
var name string
|
||||
|
||||
rows, err := driver.conn.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to fetch applied migrations",
|
||||
Command: "select_applied_migrations",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&version, &name); err != nil {
|
||||
return nil, &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to scan applied migration row",
|
||||
Command: "scan_applied_migrations",
|
||||
}
|
||||
}
|
||||
|
||||
appliedMigrations = append(appliedMigrations, &models.Migration{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Direction: models.Up,
|
||||
})
|
||||
}
|
||||
|
||||
return appliedMigrations, nil
|
||||
}
|
||||
|
||||
func currentDatabaseNameFromDB(conn *sql.Conn, config *Config) (string, error) {
|
||||
query := "SELECT DATABASE()"
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var databaseName string
|
||||
if err := conn.QueryRowContext(ctx, query).Scan(&databaseName); err != nil {
|
||||
return "", &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to fetch database name",
|
||||
Command: "current_database",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return databaseName, nil
|
||||
}
|
||||
|
||||
func mergeConfigs(config *Config, defaultConfig *Config) *Config {
|
||||
if config.MigrationsTable == "" {
|
||||
config.MigrationsTable = defaultConfig.MigrationsTable
|
||||
}
|
||||
|
||||
if config.StatementTimeoutInSecs == 0 {
|
||||
config.StatementTimeoutInSecs = defaultConfig.StatementTimeoutInSecs
|
||||
}
|
||||
|
||||
if config.MigrationMaxSize == 0 {
|
||||
config.MigrationMaxSize = defaultConfig.MigrationMaxSize
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func mergeConfigWithParams(params map[string]string, config *Config) (*Config, error) {
|
||||
var err error
|
||||
|
||||
for _, configKey := range configParams {
|
||||
if v, ok := params[configKey]; ok {
|
||||
switch configKey {
|
||||
case "x-migration-max-size":
|
||||
if config.MigrationMaxSize, err = strconv.Atoi(v); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to cast config param %s of %s", configKey, v))
|
||||
}
|
||||
case "x-migrations-table":
|
||||
config.MigrationsTable = v
|
||||
case "x-statement-timeout":
|
||||
if config.StatementTimeoutInSecs, err = strconv.Atoi(v); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to cast config param %s of %s", configKey, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (driver *mysql) addMigrationQuery(migration *models.Migration) string {
|
||||
if migration.Direction == models.Down {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE (Version=%d AND NAME='%s')", driver.config.MigrationsTable, migration.Version, migration.Name)
|
||||
}
|
||||
return fmt.Sprintf("INSERT INTO %s (Version, Name) VALUES (%d, '%s')", driver.config.MigrationsTable, migration.Version, migration.Name)
|
||||
}
|
||||
|
||||
func (driver *mysql) SetConfig(key string, value interface{}) error {
|
||||
if driver.config != nil {
|
||||
switch key {
|
||||
case "StatementTimeoutInSecs":
|
||||
n, ok := value.(int)
|
||||
if ok {
|
||||
driver.config.StatementTimeoutInSecs = n
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("incorrect value type for %s", key)
|
||||
case "MigrationsTable":
|
||||
n, ok := value.(string)
|
||||
if ok {
|
||||
driver.config.MigrationsTable = n
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("incorrect value type for %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("incorrect key name %q", key)
|
||||
}
|
||||
23
vendor/github.com/go-morph/morph/drivers/mysql/utils.go
сгенерированный
поставляемый
Обычный файл
23
vendor/github.com/go-morph/morph/drivers/mysql/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
mysqlDriver "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func ExtractMysqlDSNParams(conn string) (map[string]string, error) {
|
||||
cfg, err := mysqlDriver.ParseDSN(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg.Params, nil
|
||||
}
|
||||
|
||||
func extractDatabaseNameFromURL(conn string) (string, error) {
|
||||
cfg, err := mysqlDriver.ParseDSN(conn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cfg.DBName, nil
|
||||
}
|
||||
464
vendor/github.com/go-morph/morph/drivers/postgres/postgres.go
сгенерированный
поставляемый
Обычный файл
464
vendor/github.com/go-morph/morph/drivers/postgres/postgres.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,464 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/go-morph/morph/drivers"
|
||||
"github.com/go-morph/morph/models"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
var (
|
||||
driverName = "postgres"
|
||||
defaultConfig = &Config{
|
||||
MigrationsTable: "db_migrations",
|
||||
StatementTimeoutInSecs: 60,
|
||||
MigrationMaxSize: defaultMigrationMaxSize,
|
||||
}
|
||||
defaultMigrationMaxSize = 10 * 1 << 20 // 10 MB
|
||||
configParams = []string{
|
||||
"x-migration-max-size",
|
||||
"x-migrations-table",
|
||||
"x-statement-timeout",
|
||||
}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MigrationsTable string
|
||||
StatementTimeoutInSecs int
|
||||
MigrationMaxSize int
|
||||
databaseName string
|
||||
schemaName string
|
||||
closeDBonClose bool
|
||||
}
|
||||
|
||||
type postgres struct {
|
||||
conn *sql.Conn
|
||||
db *sql.DB
|
||||
config *Config
|
||||
}
|
||||
|
||||
func WithInstance(dbInstance *sql.DB, config *Config) (drivers.Driver, error) {
|
||||
driverConfig := mergeConfigs(config, defaultConfig)
|
||||
|
||||
conn, err := dbInstance.Conn(context.Background())
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "grabbing_connection", OrigErr: err, Message: "failed to grab connection to the database"}
|
||||
}
|
||||
|
||||
if driverConfig.databaseName, err = currentDatabaseNameFromDB(conn, driverConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if driverConfig.schemaName, err = currentSchema(conn, driverConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &postgres{
|
||||
conn: conn,
|
||||
db: dbInstance,
|
||||
config: driverConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Open(connURL string) (drivers.Driver, error) {
|
||||
customParams, err := drivers.ExtractCustomParams(connURL, configParams)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to parse custom parameters from url"}
|
||||
}
|
||||
|
||||
sanitizedConnURL, err := drivers.RemoveParamsFromURL(connURL, configParams)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to sanitize url from custom parameters"}
|
||||
}
|
||||
|
||||
driverConfig, err := mergeConfigWithParams(customParams, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to merge custom params to driver config"}
|
||||
}
|
||||
|
||||
db, err := sql.Open(driverName, sanitizedConnURL)
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "opening_connection", OrigErr: err, Message: "failed to open connection with the database"}
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{Driver: driverName, Command: "grabbing_connection", OrigErr: err, Message: "failed to grab connection to the database"}
|
||||
}
|
||||
|
||||
if driverConfig.databaseName, err = extractDatabaseNameFromURL(connURL); err != nil {
|
||||
return nil, &drivers.AppError{Driver: driverName, OrigErr: err, Message: "failed to extract database name from connection url"}
|
||||
}
|
||||
|
||||
if driverConfig.schemaName, err = currentSchema(conn, driverConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driverConfig.closeDBonClose = true
|
||||
|
||||
return &postgres{
|
||||
db: db,
|
||||
config: driverConfig,
|
||||
conn: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func currentSchema(conn *sql.Conn, config *Config) (string, error) {
|
||||
query := "SELECT CURRENT_SCHEMA()"
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var schemaName string
|
||||
if err := conn.QueryRowContext(ctx, query).Scan(&schemaName); err != nil {
|
||||
return "", &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to fetch current schema",
|
||||
Command: "current_schema",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
return schemaName, nil
|
||||
}
|
||||
|
||||
func mergeConfigWithParams(params map[string]string, config *Config) (*Config, error) {
|
||||
var err error
|
||||
|
||||
for _, configKey := range configParams {
|
||||
if v, ok := params[configKey]; ok {
|
||||
switch configKey {
|
||||
case "x-migration-max-size":
|
||||
if config.MigrationMaxSize, err = strconv.Atoi(v); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to cast config param %s of %s", configKey, v))
|
||||
}
|
||||
case "x-migrations-table":
|
||||
config.MigrationsTable = v
|
||||
case "x-statement-timeout":
|
||||
if config.StatementTimeoutInSecs, err = strconv.Atoi(v); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to cast config param %s of %s", configKey, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func mergeConfigs(config, defaultConfig *Config) *Config {
|
||||
if config.MigrationsTable == "" {
|
||||
config.MigrationsTable = defaultConfig.MigrationsTable
|
||||
}
|
||||
|
||||
if config.StatementTimeoutInSecs == 0 {
|
||||
config.StatementTimeoutInSecs = defaultConfig.StatementTimeoutInSecs
|
||||
}
|
||||
|
||||
if config.MigrationMaxSize == 0 {
|
||||
config.MigrationMaxSize = defaultConfig.MigrationMaxSize
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (pg *postgres) Ping() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return pg.conn.PingContext(ctx)
|
||||
}
|
||||
|
||||
func (pg *postgres) createSchemaTableIfNotExists() (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
createTableIfNotExistsQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (version bigint not null primary key, name varchar not null)", pg.config.MigrationsTable)
|
||||
if _, err = pg.conn.ExecContext(ctx, createTableIfNotExistsQuery); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed while executing query",
|
||||
Command: "create_migrations_table_if_not_exists",
|
||||
Query: []byte(createTableIfNotExistsQuery),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgres) Close() error {
|
||||
if pg.conn != nil {
|
||||
if err := pg.conn.Close(); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to close database connection",
|
||||
Command: "pg_conn_close",
|
||||
Query: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pg.db != nil && pg.config.closeDBonClose {
|
||||
if err := pg.db.Close(); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to close database",
|
||||
Command: "pg_db_close",
|
||||
Query: nil,
|
||||
}
|
||||
}
|
||||
pg.db = nil
|
||||
}
|
||||
|
||||
pg.conn = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgres) Lock() error {
|
||||
aid, err := drivers.GenerateAdvisoryLockID(pg.config.databaseName, pg.config.schemaName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This will wait until the lock can be acquired or until the statement timeout has reached.
|
||||
query := "SELECT pg_advisory_lock($1)"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := pg.conn.ExecContext(ctx, query, aid); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to obtain advisory lock",
|
||||
Command: "lock_migrations_table",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgres) Unlock() error {
|
||||
aid, err := drivers.GenerateAdvisoryLockID(pg.config.databaseName, pg.config.schemaName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := "SELECT pg_advisory_unlock($1)"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := pg.conn.ExecContext(ctx, query, aid); err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to unlock advisory lock",
|
||||
Command: "unlock_migrations_table",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgres) Apply(migration *models.Migration, saveVersion bool) (err error) {
|
||||
if err = pg.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// If we saw no error prior to unlocking and unlocking returns an error we need to
|
||||
// assign the unlocking error to err
|
||||
if unlockErr := pg.Unlock(); unlockErr != nil && err == nil {
|
||||
err = unlockErr
|
||||
}
|
||||
}()
|
||||
|
||||
query, readErr := migration.Query()
|
||||
if readErr != nil {
|
||||
return &drivers.AppError{
|
||||
OrigErr: readErr,
|
||||
Driver: driverName,
|
||||
Message: fmt.Sprintf("failed to read migration query: %s", migration.Name),
|
||||
}
|
||||
}
|
||||
defer migration.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
transaction, err := pg.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "error while opening a transaction to the database",
|
||||
Command: "begin_transaction",
|
||||
}
|
||||
}
|
||||
|
||||
if err = executeQuery(transaction, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if saveVersion {
|
||||
if err = executeQuery(transaction, pg.addMigrationQuery(migration)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = transaction.Commit()
|
||||
if err != nil {
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "error while committing a transaction to the database",
|
||||
Command: "commit_transaction",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgres) AppliedMigrations() (migrations []*models.Migration, err error) {
|
||||
if pg.conn == nil {
|
||||
return nil, &drivers.AppError{
|
||||
OrigErr: errors.New("driver has no connection established"),
|
||||
Message: "database connection is missing",
|
||||
Driver: driverName,
|
||||
}
|
||||
}
|
||||
|
||||
if err = pg.Lock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
// If we saw no error prior to unlocking and unlocking returns an error we need to
|
||||
// assign the unlocking error to err
|
||||
if unlockErr := pg.Unlock(); unlockErr != nil && err == nil {
|
||||
err = unlockErr
|
||||
}
|
||||
}()
|
||||
|
||||
if err := pg.createSchemaTableIfNotExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT version, name FROM %s", pg.config.MigrationsTable)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(pg.config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
var appliedMigrations []*models.Migration
|
||||
var version uint32
|
||||
var name string
|
||||
|
||||
rows, err := pg.conn.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to fetch applied migrations",
|
||||
Command: "select_applied_migrations",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&version, &name); err != nil {
|
||||
return nil, &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to scan applied migration row",
|
||||
Command: "scan_applied_migrations",
|
||||
}
|
||||
}
|
||||
|
||||
appliedMigrations = append(appliedMigrations, &models.Migration{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Direction: models.Up,
|
||||
})
|
||||
}
|
||||
|
||||
return appliedMigrations, nil
|
||||
}
|
||||
|
||||
func (pg *postgres) addMigrationQuery(migration *models.Migration) string {
|
||||
if migration.Direction == models.Down {
|
||||
return fmt.Sprintf("DELETE FROM %s WHERE (Version=%d AND NAME='%s')", pg.config.MigrationsTable, migration.Version, migration.Name)
|
||||
}
|
||||
return fmt.Sprintf("INSERT INTO %s (version, name) VALUES (%d, '%s')", pg.config.MigrationsTable, migration.Version, migration.Name)
|
||||
}
|
||||
|
||||
func executeQuery(transaction *sql.Tx, query string) error {
|
||||
if _, err := transaction.Exec(query); err != nil {
|
||||
if txErr := transaction.Rollback(); txErr != nil {
|
||||
err = errors.Wrap(errors.New(err.Error()+txErr.Error()), "failed to execute query in migration transaction")
|
||||
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Command: "rollback_transaction",
|
||||
}
|
||||
}
|
||||
|
||||
return &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to execute migration",
|
||||
Command: "executing_query",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func currentDatabaseNameFromDB(conn *sql.Conn, config *Config) (string, error) {
|
||||
query := "SELECT CURRENT_DATABASE()"
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.StatementTimeoutInSecs)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var databaseName string
|
||||
if err := conn.QueryRowContext(ctx, query).Scan(&databaseName); err != nil {
|
||||
return "", &drivers.DatabaseError{
|
||||
OrigErr: err,
|
||||
Driver: driverName,
|
||||
Message: "failed to fetch database name",
|
||||
Command: "current_database",
|
||||
Query: []byte(query),
|
||||
}
|
||||
}
|
||||
return databaseName, nil
|
||||
}
|
||||
|
||||
func (pg *postgres) SetConfig(key string, value interface{}) error {
|
||||
if pg.config != nil {
|
||||
switch key {
|
||||
case "StatementTimeoutInSecs":
|
||||
n, ok := value.(int)
|
||||
if ok {
|
||||
pg.config.StatementTimeoutInSecs = n
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("incorrect value type for %s", key)
|
||||
case "MigrationsTable":
|
||||
n, ok := value.(string)
|
||||
if ok {
|
||||
pg.config.MigrationsTable = n
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("incorrect value type for %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("incorrect key name %q", key)
|
||||
}
|
||||
12
vendor/github.com/go-morph/morph/drivers/postgres/utils.go
сгенерированный
поставляемый
Обычный файл
12
vendor/github.com/go-morph/morph/drivers/postgres/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,12 @@
|
||||
package postgres
|
||||
|
||||
import "net/url"
|
||||
|
||||
func extractDatabaseNameFromURL(URL string) (string, error) {
|
||||
uri, err := url.Parse(URL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return uri.Path[1:], nil
|
||||
}
|
||||
49
vendor/github.com/go-morph/morph/drivers/utils.go
сгенерированный
поставляемый
Обычный файл
49
vendor/github.com/go-morph/morph/drivers/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,49 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExtractCustomParams(conn string, params []string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
for _, param := range params {
|
||||
reg := regexp.MustCompile(fmt.Sprintf("%s=(\\w+)", param))
|
||||
match := reg.FindStringSubmatch(conn)
|
||||
if len(match) > 1 {
|
||||
result[param] = match[1]
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func RemoveParamsFromURL(conn string, params []string) (string, error) {
|
||||
prefixCorrection := regexp.MustCompile("\\?&+")
|
||||
repeatedAmber := regexp.MustCompile("&+")
|
||||
|
||||
for _, param := range params {
|
||||
reg := regexp.MustCompile(fmt.Sprintf("%s=\\w+", param))
|
||||
conn = string(reg.ReplaceAll([]byte(conn), []byte(``)))
|
||||
}
|
||||
|
||||
parts := strings.Split(conn, "/")
|
||||
urlParams := parts[len(parts)-1]
|
||||
|
||||
urlParams = string(prefixCorrection.ReplaceAll([]byte(urlParams), []byte(`?`)))
|
||||
urlParams = string(repeatedAmber.ReplaceAll([]byte(urlParams), []byte(`&`)))
|
||||
parts[len(parts)-1] = urlParams
|
||||
|
||||
return strings.Join(parts, "/"), nil
|
||||
}
|
||||
|
||||
const advisoryLockIDSalt uint = 1486364155
|
||||
|
||||
func GenerateAdvisoryLockID(databaseName, schemaName string) (string, error) {
|
||||
databaseName = schemaName + databaseName + "\x00"
|
||||
sum := crc32.ChecksumIEEE([]byte(databaseName))
|
||||
sum = sum * uint32(advisoryLockIDSalt)
|
||||
return fmt.Sprint(sum), nil
|
||||
}
|
||||
14
vendor/github.com/go-morph/morph/go.mod
сгенерированный
поставляемый
Обычный файл
14
vendor/github.com/go-morph/morph/go.mod
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,14 @@
|
||||
module github.com/go-morph/morph
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/dave/jennifer v1.4.1
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/lib/pq v1.10.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/stretchr/testify v1.3.0
|
||||
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 // indirect
|
||||
)
|
||||
312
vendor/github.com/go-morph/morph/go.sum
сгенерированный
поставляемый
Обычный файл
312
vendor/github.com/go-morph/morph/go.sum
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,312 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw=
|
||||
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
||||
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 h1:64ChN/hjER/taL4YJuA+gpLfIMT+/NFherRZixbxOhg=
|
||||
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
58
vendor/github.com/go-morph/morph/models/migration.go
сгенерированный
поставляемый
Обычный файл
58
vendor/github.com/go-morph/morph/models/migration.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,58 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Migration struct {
|
||||
Bytes io.ReadCloser
|
||||
Name string
|
||||
RawName string
|
||||
Version uint32
|
||||
Direction Direction
|
||||
}
|
||||
|
||||
func NewMigration(migrationBytes io.ReadCloser, fileName string) (*Migration, error) {
|
||||
m := Regex.FindStringSubmatch(fileName)
|
||||
|
||||
var (
|
||||
versionUint64 uint64
|
||||
direction Direction
|
||||
identifier string
|
||||
err error
|
||||
)
|
||||
|
||||
if len(m) == 5 {
|
||||
versionUint64, err = strconv.ParseUint(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
identifier = m[2]
|
||||
direction = Direction(m[3])
|
||||
} else {
|
||||
return nil, fmt.Errorf("could not parse file: %s", fileName)
|
||||
}
|
||||
|
||||
return &Migration{
|
||||
Version: uint32(versionUint64),
|
||||
Name: identifier,
|
||||
RawName: fileName,
|
||||
Bytes: migrationBytes,
|
||||
Direction: direction,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Migration) Query() (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := buf.ReadFrom(m.Bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (m *Migration) Close() error {
|
||||
return m.Bytes.Close()
|
||||
}
|
||||
23
vendor/github.com/go-morph/morph/models/parse.go
сгенерированный
поставляемый
Обычный файл
23
vendor/github.com/go-morph/morph/models/parse.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Direction is either up or down.
|
||||
type Direction string
|
||||
|
||||
const (
|
||||
Down Direction = "down"
|
||||
Up Direction = "up"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrParse = fmt.Errorf("no match")
|
||||
)
|
||||
|
||||
// Regex matches the following pattern:
|
||||
// 123_name.up.ext
|
||||
// 123_name.down.ext
|
||||
var Regex = regexp.MustCompile(`^([0-9]+)_(.*)\.(` + string(Down) + `|` + string(Up) + `)\.(.*)$`)
|
||||
283
vendor/github.com/go-morph/morph/morph.go
сгенерированный
поставляемый
Обычный файл
283
vendor/github.com/go-morph/morph/morph.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,283 @@
|
||||
package morph
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-morph/morph/models"
|
||||
|
||||
"github.com/go-morph/morph/drivers"
|
||||
"github.com/go-morph/morph/sources"
|
||||
|
||||
_ "github.com/go-morph/morph/drivers/mysql"
|
||||
_ "github.com/go-morph/morph/drivers/postgres"
|
||||
|
||||
_ "github.com/go-morph/morph/sources/file"
|
||||
_ "github.com/go-morph/morph/sources/go_bindata"
|
||||
)
|
||||
|
||||
// DefaultLockTimeout sets the max time a database driver has to acquire a lock.
|
||||
var DefaultLockTimeout = 15 * time.Second
|
||||
|
||||
var migrationProgressStart = "== %s: migrating ================================================="
|
||||
var migrationProgressFinished = "== %s: migrated (%s) ========================================"
|
||||
|
||||
const maxProgressLogLength = 100
|
||||
|
||||
type Morph struct {
|
||||
config *Config
|
||||
driver drivers.Driver
|
||||
source sources.Source
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Logger Logger
|
||||
LockTimeout time.Duration
|
||||
}
|
||||
|
||||
type EngineOption func(*Morph)
|
||||
|
||||
var defaultConfig = &Config{
|
||||
LockTimeout: DefaultLockTimeout,
|
||||
Logger: log.New(os.Stderr, "", log.LstdFlags), // add default logger
|
||||
}
|
||||
|
||||
func WithLogger(logger *log.Logger) EngineOption {
|
||||
return func(m *Morph) {
|
||||
m.config.Logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func WithLockTimeout(lockTimeout time.Duration) EngineOption {
|
||||
return func(m *Morph) {
|
||||
m.config.LockTimeout = lockTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func SetMigrationTableName(name string) EngineOption {
|
||||
return func(m *Morph) {
|
||||
_ = m.driver.SetConfig("MigrationsTable", name)
|
||||
}
|
||||
}
|
||||
|
||||
func SetSatementTimeoutInSeconds(n int) EngineOption {
|
||||
return func(m *Morph) {
|
||||
_ = m.driver.SetConfig("StatementTimeoutInSecs", n)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new instance of the migrations engine from an existing db instance and a migrations source
|
||||
func New(driver drivers.Driver, source sources.Source, options ...EngineOption) (*Morph, error) {
|
||||
engine := &Morph{
|
||||
config: defaultConfig,
|
||||
source: source,
|
||||
driver: driver,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(engine)
|
||||
}
|
||||
|
||||
if err := driver.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying database connection of the engine.
|
||||
func (m *Morph) Close() error {
|
||||
return m.driver.Close()
|
||||
}
|
||||
|
||||
// ApplyAll applies all pending migrations.
|
||||
func (m *Morph) ApplyAll() error {
|
||||
_, err := m.Apply(-1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Applies limited number of migrations
|
||||
func (m *Morph) Apply(limit int) (int, error) {
|
||||
appliedMigrations, err := m.driver.AppliedMigrations()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
pendingMigrations, err := computePendingMigrations(appliedMigrations, m.source.Migrations())
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
migrations, rollbacks, err := findUpScripts(sortMigrations(pendingMigrations))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
steps := limit
|
||||
if len(migrations) < steps {
|
||||
return -1, fmt.Errorf("there are only %d migrations avaliable, but you requested %d", len(migrations), steps)
|
||||
}
|
||||
|
||||
if limit < 0 {
|
||||
steps = len(migrations)
|
||||
}
|
||||
|
||||
var applied int
|
||||
for i := 0; i < steps; i++ {
|
||||
start := time.Now()
|
||||
migrationName := migrations[i].Name
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf(migrationProgressStart, migrationName))))
|
||||
if err := m.driver.Apply(migrations[i], true); err != nil {
|
||||
rollback, ok := rollbacks[migrationName]
|
||||
if ok {
|
||||
m.config.Logger.Println(ErrorLoggerLight.Sprint(formatProgress(fmt.Sprintf("failed to apply %s, rolling back.", migrationName))))
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf("trying to apply %s (%s)", rollback.Name, rollback.Direction))))
|
||||
|
||||
if err2 := m.driver.Apply(rollback, false); err2 != nil {
|
||||
return applied, fmt.Errorf("could not rollback the migration %s: %w", migrationName, err)
|
||||
}
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf("rollback completed for %s. Aborting gracefully.", migrationName))))
|
||||
return applied, err
|
||||
}
|
||||
|
||||
return applied, err
|
||||
}
|
||||
|
||||
applied++
|
||||
elapsed := time.Since(start)
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf(migrationProgressFinished, migrationName, fmt.Sprintf("%.4fs", elapsed.Seconds())))))
|
||||
}
|
||||
|
||||
return applied, nil
|
||||
}
|
||||
|
||||
// ApplyDown rollbacks a limited number of migrations
|
||||
// if limit is given below zero, all down scripts are going to be applied.
|
||||
func (m *Morph) ApplyDown(limit int) (int, error) {
|
||||
appliedMigrations, err := m.driver.AppliedMigrations()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
sortedMigrations := reverseSortMigrations(appliedMigrations)
|
||||
downMigrations, err := findDownScripts(sortedMigrations, m.source.Migrations())
|
||||
|
||||
steps := limit
|
||||
if len(sortedMigrations) < steps {
|
||||
return -1, fmt.Errorf("there are only %d migrations avaliable, but you requested %d", len(sortedMigrations), steps)
|
||||
}
|
||||
|
||||
if limit < 0 {
|
||||
steps = len(sortedMigrations)
|
||||
}
|
||||
|
||||
var applied int
|
||||
for i := 0; i < steps; i++ {
|
||||
start := time.Now()
|
||||
migrationName := sortedMigrations[i].Name
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf(migrationProgressStart, migrationName))))
|
||||
|
||||
down := downMigrations[migrationName]
|
||||
if err := m.driver.Apply(down, true); err != nil {
|
||||
return applied, err
|
||||
}
|
||||
|
||||
applied++
|
||||
elapsed := time.Since(start)
|
||||
m.config.Logger.Println(InfoLoggerLight.Sprint(formatProgress(fmt.Sprintf(migrationProgressFinished, migrationName, fmt.Sprintf("%.4fs", elapsed.Seconds())))))
|
||||
}
|
||||
|
||||
return applied, nil
|
||||
}
|
||||
|
||||
func reverseSortMigrations(migrations []*models.Migration) []*models.Migration {
|
||||
sort.Slice(migrations, func(i, j int) bool {
|
||||
return migrations[i].Version > migrations[j].Version
|
||||
})
|
||||
return migrations
|
||||
}
|
||||
|
||||
func sortMigrations(migrations []*models.Migration) []*models.Migration {
|
||||
sort.Slice(migrations, func(i, j int) bool {
|
||||
return migrations[i].RawName < migrations[j].RawName
|
||||
})
|
||||
return migrations
|
||||
}
|
||||
|
||||
func computePendingMigrations(appliedMigrations []*models.Migration, sourceMigrations []*models.Migration) ([]*models.Migration, error) {
|
||||
// sourceMigrations has to be greater or equal to databaseMigrations
|
||||
if len(appliedMigrations) > len(sourceMigrations) {
|
||||
return nil, errors.New("migration mismatch, there are more migrations applied than those were specified in source")
|
||||
}
|
||||
|
||||
dict := make(map[string]*models.Migration)
|
||||
for _, appliedMigration := range appliedMigrations {
|
||||
dict[appliedMigration.Name] = appliedMigration
|
||||
}
|
||||
|
||||
var pendingMigrations []*models.Migration
|
||||
for _, sourceMigration := range sourceMigrations {
|
||||
if _, ok := dict[sourceMigration.Name]; !ok {
|
||||
pendingMigrations = append(pendingMigrations, sourceMigration)
|
||||
}
|
||||
}
|
||||
|
||||
return pendingMigrations, nil
|
||||
}
|
||||
|
||||
func findUpScripts(migrations []*models.Migration) ([]*models.Migration, map[string]*models.Migration, error) {
|
||||
rollbackMigrations := make(map[string]*models.Migration)
|
||||
toBeAppliedMigrations := make([]*models.Migration, 0)
|
||||
for _, migration := range migrations {
|
||||
if migration.Direction != models.Up {
|
||||
rollbackMigrations[migration.Name] = migration
|
||||
continue
|
||||
}
|
||||
toBeAppliedMigrations = append(toBeAppliedMigrations, migration)
|
||||
}
|
||||
|
||||
for _, migration := range toBeAppliedMigrations {
|
||||
_, ok := rollbackMigrations[migration.Name]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("the rollback migration file for %s is missing", migration.RawName)
|
||||
}
|
||||
}
|
||||
|
||||
return toBeAppliedMigrations, rollbackMigrations, nil
|
||||
}
|
||||
|
||||
func findDownScripts(appliedMigrations []*models.Migration, sourceMigrations []*models.Migration) (map[string]*models.Migration, error) {
|
||||
tmp := make(map[string]*models.Migration)
|
||||
for _, m := range sourceMigrations {
|
||||
if m.Direction != models.Down {
|
||||
continue
|
||||
}
|
||||
tmp[m.Name] = m
|
||||
}
|
||||
|
||||
for _, m := range appliedMigrations {
|
||||
_, ok := tmp[m.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not find down script for %s", m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return tmp, nil
|
||||
}
|
||||
|
||||
func formatProgress(p string) string {
|
||||
if len(p) < maxProgressLogLength {
|
||||
return p + strings.Repeat("=", maxProgressLogLength-len(p))
|
||||
}
|
||||
|
||||
if len(p) > maxProgressLogLength {
|
||||
return p[:maxProgressLogLength]
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
105
vendor/github.com/go-morph/morph/sources/file/file.go
сгенерированный
поставляемый
Обычный файл
105
vendor/github.com/go-morph/morph/sources/file/file.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,105 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-morph/morph/models"
|
||||
"github.com/go-morph/morph/sources"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sources.Register("file", &File{})
|
||||
}
|
||||
|
||||
type File struct {
|
||||
url string
|
||||
path string
|
||||
migrations []*models.Migration
|
||||
}
|
||||
|
||||
func (f *File) Open(sourceURL string) (sources.Source, error) {
|
||||
uri, err := url.Parse(sourceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// host might be "." for relative URLs like file://./migrations
|
||||
p := uri.Opaque
|
||||
if len(p) == 0 {
|
||||
p = uri.Host + uri.Path
|
||||
}
|
||||
|
||||
// if no path provided, default to current directory
|
||||
if len(p) == 0 {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = wd
|
||||
} else if p[0:1] != "/" {
|
||||
// make path absolute if required
|
||||
abs, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = abs
|
||||
}
|
||||
|
||||
nf := &File{
|
||||
url: sourceURL,
|
||||
path: p,
|
||||
}
|
||||
|
||||
if err := nf.readMigrations(); err != nil {
|
||||
return nil, fmt.Errorf("cannot read migrations in path %q: %w", p, err)
|
||||
}
|
||||
|
||||
return nf, nil
|
||||
}
|
||||
|
||||
func (f *File) readMigrations() error {
|
||||
info, err := os.Stat(f.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return fmt.Errorf("file %q is not a directory", info.Name())
|
||||
}
|
||||
|
||||
migrations := []*models.Migration{}
|
||||
walkerr := filepath.Walk(f.path, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := models.NewMigration(file, filepath.Base(path))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create migration: %w", err)
|
||||
}
|
||||
|
||||
migrations = append(migrations, m)
|
||||
return nil
|
||||
})
|
||||
if walkerr != nil {
|
||||
return walkerr
|
||||
}
|
||||
|
||||
f.migrations = migrations
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Migrations() []*models.Migration {
|
||||
return f.migrations
|
||||
}
|
||||
31
vendor/github.com/go-morph/morph/sources/go_bindata/README.md
сгенерированный
поставляемый
Обычный файл
31
vendor/github.com/go-morph/morph/sources/go_bindata/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
# go-bindata source
|
||||
|
||||
This source reads migrations from a
|
||||
[go-bindata](github.com/go-bindata/go-bindata) embedded binary file.
|
||||
|
||||
## Usage
|
||||
|
||||
To read the embedded data, create a migration source through the
|
||||
`WithInstance` method and then instantiate `morph`:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/go-morph/morph"
|
||||
"github.com/go-morph/morph/sources/go_bindata"
|
||||
"github.com/go-morph/morph/sources/go_bindata/testdata"
|
||||
)
|
||||
|
||||
func main() {
|
||||
res := bindata.Resource(testdata.AssetNames(), func(name string) ([]byte, error) {
|
||||
return testdata.Asset(name)
|
||||
})
|
||||
|
||||
src, err := bindata.WithInstance(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create the morph instance from the source and driver
|
||||
m := morph.NewFromConnURL("postgres://...", src, opts)
|
||||
}
|
||||
```
|
||||
68
vendor/github.com/go-morph/morph/sources/go_bindata/go-bindata.go
сгенерированный
поставляемый
Обычный файл
68
vendor/github.com/go-morph/morph/sources/go_bindata/go-bindata.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,68 @@
|
||||
package bindata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/go-morph/morph/models"
|
||||
"github.com/go-morph/morph/sources"
|
||||
)
|
||||
|
||||
type AssetFunc func(name string) ([]byte, error)
|
||||
|
||||
func Resource(names []string, fn AssetFunc) *AssetSource {
|
||||
return &AssetSource{
|
||||
Names: names,
|
||||
AssetFunc: fn,
|
||||
}
|
||||
}
|
||||
|
||||
type AssetSource struct {
|
||||
Names []string
|
||||
AssetFunc AssetFunc
|
||||
}
|
||||
|
||||
func init() {
|
||||
sources.Register("go-bindata", &Bindata{})
|
||||
}
|
||||
|
||||
type Bindata struct {
|
||||
assetSource *AssetSource
|
||||
migrations []*models.Migration
|
||||
}
|
||||
|
||||
func (b *Bindata) Open(url string) (sources.Source, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func WithInstance(assetSource *AssetSource) (sources.Source, error) {
|
||||
b := &Bindata{
|
||||
assetSource: assetSource,
|
||||
migrations: []*models.Migration{},
|
||||
}
|
||||
|
||||
for _, filename := range assetSource.Names {
|
||||
migrationBytes, err := b.assetSource.AssetFunc(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read migration %q: %w", filename, err)
|
||||
}
|
||||
|
||||
m, err := models.NewMigration(ioutil.NopCloser(bytes.NewReader(migrationBytes)), filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create migration: %w", err)
|
||||
}
|
||||
|
||||
b.migrations = append(b.migrations, m)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *Bindata) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bindata) Migrations() []*models.Migration {
|
||||
return b.migrations
|
||||
}
|
||||
47
vendor/github.com/go-morph/morph/sources/source.go
сгенерированный
поставляемый
Обычный файл
47
vendor/github.com/go-morph/morph/sources/source.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,47 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/go-morph/morph/models"
|
||||
)
|
||||
|
||||
var sourcesMu sync.RWMutex
|
||||
var registeredSources = make(map[string]Source)
|
||||
|
||||
type Source interface {
|
||||
Open(sourceURL string) (source Source, err error)
|
||||
Close() (err error)
|
||||
Migrations() (migrations []*models.Migration)
|
||||
}
|
||||
|
||||
func Register(name string, source Source) {
|
||||
sourcesMu.Lock()
|
||||
defer sourcesMu.Unlock()
|
||||
|
||||
registeredSources[name] = source
|
||||
}
|
||||
|
||||
func List() []string {
|
||||
sourcesMu.Lock()
|
||||
defer sourcesMu.Unlock()
|
||||
|
||||
sources := make([]string, 0, len(registeredSources))
|
||||
for source := range registeredSources {
|
||||
sources = append(sources, source)
|
||||
}
|
||||
|
||||
return sources
|
||||
}
|
||||
|
||||
func Open(sourceName, sourceURL string) (Source, error) {
|
||||
sourcesMu.RLock()
|
||||
source, ok := registeredSources[sourceName]
|
||||
sourcesMu.RUnlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported source %q found", sourceName)
|
||||
}
|
||||
|
||||
return source.Open(sourceURL)
|
||||
}
|
||||
12
vendor/github.com/golang-migrate/migrate/v4/.dockerignore
сгенерированный
поставляемый
12
vendor/github.com/golang-migrate/migrate/v4/.dockerignore
сгенерированный
поставляемый
@@ -1,12 +0,0 @@
|
||||
# Project
|
||||
FAQ.md
|
||||
README.md
|
||||
LICENSE
|
||||
.gitignore
|
||||
.travis.yml
|
||||
CONTRIBUTING.md
|
||||
MIGRATIONS.md
|
||||
docker-deploy.sh
|
||||
|
||||
# Golang
|
||||
testing
|
||||
10
vendor/github.com/golang-migrate/migrate/v4/.gitignore
сгенерированный
поставляемый
10
vendor/github.com/golang-migrate/migrate/v4/.gitignore
сгенерированный
поставляемый
@@ -1,10 +0,0 @@
|
||||
.DS_Store
|
||||
cli/build
|
||||
cli/cli
|
||||
cli/migrate
|
||||
.coverage
|
||||
.godoc.pid
|
||||
vendor/
|
||||
.vscode/
|
||||
.idea
|
||||
dist/
|
||||
26
vendor/github.com/golang-migrate/migrate/v4/.golangci.yml
сгенерированный
поставляемый
26
vendor/github.com/golang-migrate/migrate/v4/.golangci.yml
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
run:
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
#- golint
|
||||
- interfacer
|
||||
- unconvert
|
||||
#- dupl
|
||||
- goconst
|
||||
- gofmt
|
||||
- misspell
|
||||
- unparam
|
||||
- nakedret
|
||||
- prealloc
|
||||
#- gosec
|
||||
linters-settings:
|
||||
misspell:
|
||||
locale: US
|
||||
issues:
|
||||
max-same-issues: 0
|
||||
max-issues-per-linter: 0
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
# gosec: Duplicated errcheck checks
|
||||
- G104
|
||||
102
vendor/github.com/golang-migrate/migrate/v4/.goreleaser.yml
сгенерированный
поставляемый
102
vendor/github.com/golang-migrate/migrate/v4/.goreleaser.yml
сгенерированный
поставляемый
@@ -1,102 +0,0 @@
|
||||
project_name: migrate
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- 386
|
||||
goarm:
|
||||
- 7
|
||||
main: ./cmd/migrate
|
||||
ldflags:
|
||||
- '-w -s -X main.Version={{ .Version }} -extldflags "static"'
|
||||
flags:
|
||||
- "-tags={{ .Env.DATABASE }} {{ .Env.SOURCE }}"
|
||||
- "-trimpath"
|
||||
nfpms:
|
||||
- homepage: "https://github.com/golang-migrate/migrate"
|
||||
maintainer: "dhui@users.noreply.github.com"
|
||||
license: MIT
|
||||
description: "Database migrations"
|
||||
formats:
|
||||
- deb
|
||||
file_name_template: "{{ .ProjectName }}.{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
dockers:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
dockerfile: Dockerfile.github-actions
|
||||
use: buildx
|
||||
ids:
|
||||
- migrate
|
||||
image_templates:
|
||||
- 'migrate/migrate:{{ .Tag }}-amd64'
|
||||
build_flag_templates:
|
||||
- '--label=org.opencontainers.image.created={{ .Date }}'
|
||||
- '--label=org.opencontainers.image.title={{ .ProjectName }}'
|
||||
- '--label=org.opencontainers.image.revision={{ .FullCommit }}'
|
||||
- '--label=org.opencontainers.image.version={{ .Version }}'
|
||||
- "--label=org.opencontainers.image.source={{ .GitURL }}"
|
||||
- "--platform=linux/amd64"
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
dockerfile: Dockerfile.github-actions
|
||||
use: buildx
|
||||
ids:
|
||||
- migrate
|
||||
image_templates:
|
||||
- 'migrate/migrate:{{ .Tag }}-arm64'
|
||||
build_flag_templates:
|
||||
- '--label=org.opencontainers.image.created={{ .Date }}'
|
||||
- '--label=org.opencontainers.image.title={{ .ProjectName }}'
|
||||
- '--label=org.opencontainers.image.revision={{ .FullCommit }}'
|
||||
- '--label=org.opencontainers.image.version={{ .Version }}'
|
||||
- "--label=org.opencontainers.image.source={{ .GitURL }}"
|
||||
- "--platform=linux/arm64"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: 'migrate/migrate:{{ .Tag }}'
|
||||
image_templates:
|
||||
- 'migrate/migrate:{{ .Tag }}-amd64'
|
||||
- 'migrate/migrate:{{ .Tag }}-arm64'
|
||||
- name_template: 'migrate/migrate:{{ .Major }}'
|
||||
image_templates:
|
||||
- 'migrate/migrate:{{ .Tag }}-amd64'
|
||||
- 'migrate/migrate:{{ .Tag }}-arm64'
|
||||
- name_template: 'migrate/migrate:latest'
|
||||
image_templates:
|
||||
- 'migrate/migrate:{{ .Tag }}-amd64'
|
||||
- 'migrate/migrate:{{ .Tag }}-arm64'
|
||||
archives:
|
||||
- name_template: "{{ .ProjectName }}.{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
checksum:
|
||||
name_template: 'sha256sum.txt'
|
||||
release:
|
||||
draft: true
|
||||
prerelease: auto
|
||||
source:
|
||||
enabled: true
|
||||
format: zip
|
||||
changelog:
|
||||
skip: false
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- Merge pull request
|
||||
- Merge branch
|
||||
- go mod tidy
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
138
vendor/github.com/golang-migrate/migrate/v4/.travis.yml
сгенерированный
поставляемый
138
vendor/github.com/golang-migrate/migrate/v4/.travis.yml
сгенерированный
поставляемый
@@ -1,138 +0,0 @@
|
||||
language: go
|
||||
sudo: required
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
include:
|
||||
# Supported versions of Go: https://golang.org/dl/
|
||||
- go: "1.14.x"
|
||||
- go: "1.15.x"
|
||||
- go: master
|
||||
|
||||
go_import_path: github.com/golang-migrate/migrate
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
- MIGRATE_TEST_CONTAINER_BOOT_TIMEOUT=60
|
||||
- DOCKER_USERNAME=golangmigrate
|
||||
- secure: "oSOznzUrgr5h45qW4PONkREpisPAt40tnM+KFWtS/Ggu5UI2Ie0CmyYXWuBjbt7B97a4yN9Qzmn8FxJHJ7kk+ABOi3muhkxeIhr6esXbzHhX/Jhv0mj1xkzX7KoVN9oHBz3cOI/QeRyEAO68xjDHNE2kby4RTT9VBt6TQUakKVkqI5qkqLBTADepCjVC+9XhxVxUNyeWKU8ormaUfJBjoNVoDlwXekUPnJenfmfZqXxUInvBCfUyp7Pq+kurBORmg4yc6qOlRYuK67Xw+i5xpjbZouNlXPk0rq7pPy5zjhmZQ3kImoFPvNMeKViDcI6kSIJKtjdhms9/g/6MgXS9HlL5kFy8tYKbsyiHnHB1BsvaLAKXctbUZFDPstgMPADfnad2kZXPrNqIhfWKZrGRWidawCYJ1sKKwYxLMKrtA0umqgMoL90MmBOELhuGmvMV0cFJB+zo+K2YWjEiMGd8xRb5mC5aAy0ZcCehO46jGtpr217EJmMF8Ywr7cFqM2Shg5U2jev9qUpYiXwmPnJKDuoT2ZHuHmPgFIkYiWC5yeJnnmG5bed1sKBp93AFrJX+1Rx5oC4BpNegewmBZKpOSwls/D1uMAeQK3dPmQHLsT6o2VBLfeDGr+zY0R85ywwPZCv00vGol02zYoTqN7eFqr6Qhjr/qx5K1nnxJdFK3Ts="
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/pkg
|
||||
|
||||
|
||||
before_install:
|
||||
# Update docker to latest version: https://docs.travis-ci.com/user/docker/#installing-a-newer-docker-version
|
||||
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
|
||||
# Install golangci-lint
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
|
||||
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
|
||||
|
||||
install:
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- golangci-lint run
|
||||
- make test COVERAGE_DIR=/tmp/coverage
|
||||
|
||||
after_success:
|
||||
- goveralls -service=travis-ci -coverprofile /tmp/coverage/combined.txt
|
||||
- make list-external-deps > dependency_tree.txt && cat dependency_tree.txt
|
||||
- make build-cli
|
||||
- gem install --no-document fpm
|
||||
- fpm -s dir -t deb -n migrate -v "$(git describe --tags 2>/dev/null | cut -c 2-)" --license MIT -m dhui@users.noreply.github.com --url https://github.com/golang-migrate/migrate --description='Database migrations' -a amd64 -p migrate.$(git describe --tags 2>/dev/null | cut -c 2-).deb --deb-no-default-config-files -f -C cli/build migrate.linux-amd64=/usr/local/bin/migrate
|
||||
|
||||
deploy:
|
||||
- provider: releases
|
||||
api_key:
|
||||
secure: hWH1HLPpzpfA8pXQ93T1qKQVFSpQp0as/JLQ7D91jHuJ8p+RxVeqblDrR6HQY/95R/nyiE9GJmvUolSuw5h449LSrGxPtVWhdh6EnkxlQHlen5XeMhVjRjFV0sE9qGe8v7uAkiTfRO61ktTWHrEAvw5qpyqnNISodmZS78XIasPODQbNlzwINhWhDTHIjXGb4FpizYaL3OGCanrxfR9fQyCaqKGGBjRq3Mfq8U6Yd4mApmsE+uJxgaZV8K5zBqpkSzQRWhcVGNL5DuLsU3gfSJOo7kZeA2G71SHffH577dBoqtCZ4VFv169CoUZehLWCb+7XKJZmHXVujCURATSySLGUOPc6EoLFAn3YtsCA04mS4bZVo5FZPWVwfhjmkhtDR4f6wscKp7r1HsFHSOgm59QfETQdrn4MnZ44H2Jd39axqndn5DvK9EcZVjPHynOPnueXP2u6mTuUgh2VyyWBCDO3CNo0fGlo7VJI69IkIWNSD87K9cHZWYMClyKZkUzS+PmRAhHRYbVd+9ZjKOmnU36kUHNDG/ft1D4ogsY+rhVtXB4lgWDM5adri+EIScYdYnB1/pQexLBigcJY9uE7nQTR0U6QgVNYvun7uRNs40E0c4voSfmPdFO0FlOD2y1oQhnaXfWLbu9nMcTcs4RFGrcC7NzkUN4/WjG8s285V6w=
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
file:
|
||||
- cli/build/migrate.linux-amd64.tar.gz
|
||||
- cli/build/migrate.linux-armv7.tar.gz
|
||||
- cli/build/migrate.linux-arm64.tar.gz
|
||||
- cli/build/migrate.darwin-amd64.tar.gz
|
||||
- cli/build/migrate.windows-amd64.exe.tar.gz
|
||||
- cli/build/migrate.windows-386.exe.tar.gz
|
||||
- cli/build/sha256sum.txt
|
||||
- dependency_tree.txt
|
||||
- provider: packagecloud
|
||||
repository: migrate
|
||||
username: golang-migrate
|
||||
token:
|
||||
secure: aICwu3gJ1sJ1QVCD3elpg+Jxzt4P+Zj1uoh5f0sOwnjDNIZ4FwUT1cMrWloP8P2KD0iyCOawuZER27o/kQ21oX2OxHvQbYPReA2znLm7lHzCmypAAOHPxpgnQ4rMGHHJXd+OsxtdclGs67c+EbdBfoRRbK400Qz/vjPJEDeH4mh02ZHC2nw4Nk/wV4jjBIkIt9dGEx6NgOA17FCMa3MaPHlHeFIzU7IfTlDHbS0mCCYbg/wafWBWcbGqtZLWAYtJDmfjrAStmDLdAX5J5PsB7taGSGPZHmPmpGoVgrKt/tb9Xz1rFBGslTpGROOiO4CiMAvkEKFn8mxrBGjfSBqp7Dp3eeSalKXB1DJAbEXx2sEbMcvmnoR9o43meaAn+ZRts8lRL8S/skBloe6Nk8bx3NlJCGB9WPK1G56b7c/fZnJxQbrCw6hxDfbZwm8S2YPviFTo/z1BfZDhRsL74reKsN2kgnGo2W/k38vvzIpsssQ9DHN1b0TLCxolCNPtQ7oHcQ1ohcjP2UgYXk0FhqDoL+9LQva/DU4N9sKH0UbAaqsMVSErLeG8A4aauuFcVrWRBaDYyTag4dQqzTulEy7iru2kDDIBgSQ1gMW/yoBOIPK4oi6MtbTf1X39fzXFLS1cDd3LW61yAu3YrbjAetpfx2frIvrRAiL9TxWA1gnrs5o=
|
||||
dist: ubuntu/xenial
|
||||
package_glob: '*.deb'
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
- provider: packagecloud
|
||||
repository: migrate
|
||||
username: golang-migrate
|
||||
token:
|
||||
secure: aICwu3gJ1sJ1QVCD3elpg+Jxzt4P+Zj1uoh5f0sOwnjDNIZ4FwUT1cMrWloP8P2KD0iyCOawuZER27o/kQ21oX2OxHvQbYPReA2znLm7lHzCmypAAOHPxpgnQ4rMGHHJXd+OsxtdclGs67c+EbdBfoRRbK400Qz/vjPJEDeH4mh02ZHC2nw4Nk/wV4jjBIkIt9dGEx6NgOA17FCMa3MaPHlHeFIzU7IfTlDHbS0mCCYbg/wafWBWcbGqtZLWAYtJDmfjrAStmDLdAX5J5PsB7taGSGPZHmPmpGoVgrKt/tb9Xz1rFBGslTpGROOiO4CiMAvkEKFn8mxrBGjfSBqp7Dp3eeSalKXB1DJAbEXx2sEbMcvmnoR9o43meaAn+ZRts8lRL8S/skBloe6Nk8bx3NlJCGB9WPK1G56b7c/fZnJxQbrCw6hxDfbZwm8S2YPviFTo/z1BfZDhRsL74reKsN2kgnGo2W/k38vvzIpsssQ9DHN1b0TLCxolCNPtQ7oHcQ1ohcjP2UgYXk0FhqDoL+9LQva/DU4N9sKH0UbAaqsMVSErLeG8A4aauuFcVrWRBaDYyTag4dQqzTulEy7iru2kDDIBgSQ1gMW/yoBOIPK4oi6MtbTf1X39fzXFLS1cDd3LW61yAu3YrbjAetpfx2frIvrRAiL9TxWA1gnrs5o=
|
||||
dist: ubuntu/bionic
|
||||
package_glob: '*.deb'
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
- provider: packagecloud
|
||||
repository: migrate
|
||||
username: golang-migrate
|
||||
token:
|
||||
secure: aICwu3gJ1sJ1QVCD3elpg+Jxzt4P+Zj1uoh5f0sOwnjDNIZ4FwUT1cMrWloP8P2KD0iyCOawuZER27o/kQ21oX2OxHvQbYPReA2znLm7lHzCmypAAOHPxpgnQ4rMGHHJXd+OsxtdclGs67c+EbdBfoRRbK400Qz/vjPJEDeH4mh02ZHC2nw4Nk/wV4jjBIkIt9dGEx6NgOA17FCMa3MaPHlHeFIzU7IfTlDHbS0mCCYbg/wafWBWcbGqtZLWAYtJDmfjrAStmDLdAX5J5PsB7taGSGPZHmPmpGoVgrKt/tb9Xz1rFBGslTpGROOiO4CiMAvkEKFn8mxrBGjfSBqp7Dp3eeSalKXB1DJAbEXx2sEbMcvmnoR9o43meaAn+ZRts8lRL8S/skBloe6Nk8bx3NlJCGB9WPK1G56b7c/fZnJxQbrCw6hxDfbZwm8S2YPviFTo/z1BfZDhRsL74reKsN2kgnGo2W/k38vvzIpsssQ9DHN1b0TLCxolCNPtQ7oHcQ1ohcjP2UgYXk0FhqDoL+9LQva/DU4N9sKH0UbAaqsMVSErLeG8A4aauuFcVrWRBaDYyTag4dQqzTulEy7iru2kDDIBgSQ1gMW/yoBOIPK4oi6MtbTf1X39fzXFLS1cDd3LW61yAu3YrbjAetpfx2frIvrRAiL9TxWA1gnrs5o=
|
||||
dist: ubuntu/focal
|
||||
package_glob: '*.deb'
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
- provider: packagecloud
|
||||
repository: migrate
|
||||
username: golang-migrate
|
||||
token:
|
||||
secure: aICwu3gJ1sJ1QVCD3elpg+Jxzt4P+Zj1uoh5f0sOwnjDNIZ4FwUT1cMrWloP8P2KD0iyCOawuZER27o/kQ21oX2OxHvQbYPReA2znLm7lHzCmypAAOHPxpgnQ4rMGHHJXd+OsxtdclGs67c+EbdBfoRRbK400Qz/vjPJEDeH4mh02ZHC2nw4Nk/wV4jjBIkIt9dGEx6NgOA17FCMa3MaPHlHeFIzU7IfTlDHbS0mCCYbg/wafWBWcbGqtZLWAYtJDmfjrAStmDLdAX5J5PsB7taGSGPZHmPmpGoVgrKt/tb9Xz1rFBGslTpGROOiO4CiMAvkEKFn8mxrBGjfSBqp7Dp3eeSalKXB1DJAbEXx2sEbMcvmnoR9o43meaAn+ZRts8lRL8S/skBloe6Nk8bx3NlJCGB9WPK1G56b7c/fZnJxQbrCw6hxDfbZwm8S2YPviFTo/z1BfZDhRsL74reKsN2kgnGo2W/k38vvzIpsssQ9DHN1b0TLCxolCNPtQ7oHcQ1ohcjP2UgYXk0FhqDoL+9LQva/DU4N9sKH0UbAaqsMVSErLeG8A4aauuFcVrWRBaDYyTag4dQqzTulEy7iru2kDDIBgSQ1gMW/yoBOIPK4oi6MtbTf1X39fzXFLS1cDd3LW61yAu3YrbjAetpfx2frIvrRAiL9TxWA1gnrs5o=
|
||||
dist: debian/stretch
|
||||
package_glob: '*.deb'
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
- provider: packagecloud
|
||||
repository: migrate
|
||||
username: golang-migrate
|
||||
token:
|
||||
secure: aICwu3gJ1sJ1QVCD3elpg+Jxzt4P+Zj1uoh5f0sOwnjDNIZ4FwUT1cMrWloP8P2KD0iyCOawuZER27o/kQ21oX2OxHvQbYPReA2znLm7lHzCmypAAOHPxpgnQ4rMGHHJXd+OsxtdclGs67c+EbdBfoRRbK400Qz/vjPJEDeH4mh02ZHC2nw4Nk/wV4jjBIkIt9dGEx6NgOA17FCMa3MaPHlHeFIzU7IfTlDHbS0mCCYbg/wafWBWcbGqtZLWAYtJDmfjrAStmDLdAX5J5PsB7taGSGPZHmPmpGoVgrKt/tb9Xz1rFBGslTpGROOiO4CiMAvkEKFn8mxrBGjfSBqp7Dp3eeSalKXB1DJAbEXx2sEbMcvmnoR9o43meaAn+ZRts8lRL8S/skBloe6Nk8bx3NlJCGB9WPK1G56b7c/fZnJxQbrCw6hxDfbZwm8S2YPviFTo/z1BfZDhRsL74reKsN2kgnGo2W/k38vvzIpsssQ9DHN1b0TLCxolCNPtQ7oHcQ1ohcjP2UgYXk0FhqDoL+9LQva/DU4N9sKH0UbAaqsMVSErLeG8A4aauuFcVrWRBaDYyTag4dQqzTulEy7iru2kDDIBgSQ1gMW/yoBOIPK4oi6MtbTf1X39fzXFLS1cDd3LW61yAu3YrbjAetpfx2frIvrRAiL9TxWA1gnrs5o=
|
||||
dist: debian/buster
|
||||
package_glob: '*.deb'
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
- provider: script
|
||||
script: ./docker-deploy.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
go: "1.15.x"
|
||||
repo: golang-migrate/migrate
|
||||
tags: true
|
||||
24
vendor/github.com/golang-migrate/migrate/v4/CONTRIBUTING.md
сгенерированный
поставляемый
24
vendor/github.com/golang-migrate/migrate/v4/CONTRIBUTING.md
сгенерированный
поставляемый
@@ -1,24 +0,0 @@
|
||||
# Development, Testing and Contributing
|
||||
|
||||
1. Make sure you have a running Docker daemon
|
||||
(Install for [MacOS](https://docs.docker.com/docker-for-mac/))
|
||||
1. Use a version of Go that supports [modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) (e.g. Go 1.11+)
|
||||
1. Fork this repo and `git clone` somewhere to `$GOPATH/src/github.com/golang-migrate/migrate`
|
||||
* Ensure that [Go modules are enabled](https://golang.org/cmd/go/#hdr-Preliminary_module_support) (e.g. your repo path or the `GO111MODULE` environment variable are set correctly)
|
||||
1. Install [golangci-lint](https://github.com/golangci/golangci-lint#install)
|
||||
1. Run the linter: `golangci-lint run`
|
||||
1. Confirm tests are working: `make test-short`
|
||||
1. Write awesome code ...
|
||||
1. `make test` to run all tests against all database versions
|
||||
1. Push code and open Pull Request
|
||||
|
||||
Some more helpful commands:
|
||||
|
||||
* You can specify which database/ source tests to run:
|
||||
`make test-short SOURCE='file go_bindata' DATABASE='postgres cassandra'`
|
||||
* After `make test`, run `make html-coverage` which opens a shiny test coverage overview.
|
||||
* `make build-cli` builds the CLI in directory `cli/build/`.
|
||||
* `make list-external-deps` lists all external dependencies for each package
|
||||
* `make docs && make open-docs` opens godoc in your browser, `make kill-docs` kills the godoc server.
|
||||
Repeatedly call `make docs` to refresh the server.
|
||||
* Set the `DOCKER_API_VERSION` environment variable to the latest supported version if you get errors regarding the docker client API version being too new.
|
||||
26
vendor/github.com/golang-migrate/migrate/v4/Dockerfile
сгенерированный
поставляемый
26
vendor/github.com/golang-migrate/migrate/v4/Dockerfile
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
FROM golang:1.16-alpine3.13 AS builder
|
||||
ARG VERSION
|
||||
|
||||
RUN apk add --no-cache git gcc musl-dev make
|
||||
|
||||
WORKDIR /go/src/github.com/golang-migrate/migrate
|
||||
|
||||
ENV GO111MODULE=on
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN make build-docker
|
||||
|
||||
FROM alpine:3.13
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
|
||||
COPY --from=builder /go/src/github.com/golang-migrate/migrate/build/migrate.linux-386 /usr/local/bin/migrate
|
||||
RUN ln -s /usr/local/bin/migrate /migrate
|
||||
|
||||
ENTRYPOINT ["migrate"]
|
||||
CMD ["--help"]
|
||||
17
vendor/github.com/golang-migrate/migrate/v4/Dockerfile.circleci
сгенерированный
поставляемый
17
vendor/github.com/golang-migrate/migrate/v4/Dockerfile.circleci
сгенерированный
поставляемый
@@ -1,17 +0,0 @@
|
||||
ARG DOCKER_IMAGE
|
||||
FROM $DOCKER_IMAGE
|
||||
|
||||
RUN apk add --no-cache git gcc musl-dev make
|
||||
|
||||
WORKDIR /go/src/github.com/golang-migrate/migrate
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV COVERAGE_DIR=/tmp/coverage
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY . ./
|
||||
|
||||
CMD ["make", "test"]
|
||||
11
vendor/github.com/golang-migrate/migrate/v4/Dockerfile.github-actions
сгенерированный
поставляемый
11
vendor/github.com/golang-migrate/migrate/v4/Dockerfile.github-actions
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
FROM alpine:3.13
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
|
||||
COPY migrate /usr/local/bin/migrate
|
||||
|
||||
RUN ln -s /usr/local/bin/migrate /usr/bin/migrate
|
||||
RUN ln -s /usr/local/bin/migrate /migrate
|
||||
|
||||
ENTRYPOINT ["migrate"]
|
||||
CMD ["--help"]
|
||||
79
vendor/github.com/golang-migrate/migrate/v4/FAQ.md
сгенерированный
поставляемый
79
vendor/github.com/golang-migrate/migrate/v4/FAQ.md
сгенерированный
поставляемый
@@ -1,79 +0,0 @@
|
||||
# FAQ
|
||||
|
||||
#### How is the code base structured?
|
||||
```
|
||||
/ package migrate (the heart of everything)
|
||||
/cli the CLI wrapper
|
||||
/database database driver and sub directories have the actual driver implementations
|
||||
/source source driver and sub directories have the actual driver implementations
|
||||
```
|
||||
|
||||
#### Why is there no `source/driver.go:Last()`?
|
||||
It's not needed. And unless the source has a "native" way to read a directory in reversed order,
|
||||
it might be expensive to do a full directory scan in order to get the last element.
|
||||
|
||||
#### What is a NilMigration? NilVersion?
|
||||
NilMigration defines a migration without a body. NilVersion is defined as const -1.
|
||||
|
||||
#### What is the difference between uint(version) and int(targetVersion)?
|
||||
version refers to an existing migration version coming from a source and therefore can never be negative.
|
||||
targetVersion can either be a version OR represent a NilVersion, which equals -1.
|
||||
|
||||
#### What's the difference between Next/Previous and Up/Down?
|
||||
```
|
||||
1_first_migration.up.extension next -> 2_second_migration.up.extension ...
|
||||
1_first_migration.down.extension <- previous 2_second_migration.down.extension ...
|
||||
```
|
||||
|
||||
#### Why two separate files (up and down) for a migration?
|
||||
It makes all of our lives easier. No new markup/syntax to learn for users
|
||||
and existing database utility tools continue to work as expected.
|
||||
|
||||
#### How many migrations can migrate handle?
|
||||
Whatever the maximum positive signed integer value is for your platform.
|
||||
For 32bit it would be 2,147,483,647 migrations. Migrate only keeps references to
|
||||
the currently run and pre-fetched migrations in memory. Please note that some
|
||||
source drivers need to do build a full "directory" tree first, which puts some
|
||||
heat on the memory consumption.
|
||||
|
||||
#### Are the table tests in migrate_test.go bloated?
|
||||
Yes and no. There are duplicate test cases for sure but they don't hurt here. In fact
|
||||
the tests are very visual now and might help new users understand expected behaviors quickly.
|
||||
Migrate from version x to y and y is the last migration? Just check out the test for
|
||||
that particular case and know what's going on instantly.
|
||||
|
||||
#### What is Docker being used for?
|
||||
Only for testing. See [testing/docker.go](testing/docker.go)
|
||||
|
||||
#### Why not just use docker-compose?
|
||||
It doesn't give us enough runtime control for testing. We want to be able to bring up containers fast
|
||||
and whenever we want, not just once at the beginning of all tests.
|
||||
|
||||
#### Can I maintain my driver in my own repository?
|
||||
Yes, technically thats possible. We want to encourage you to contribute your driver to this respository though.
|
||||
The driver's functionality is dictated by migrate's interfaces. That means there should really
|
||||
just be one driver for a database/ source. We want to prevent a future where several drivers doing the exact same thing,
|
||||
just implemented a bit differently, co-exist somewhere on GitHub. If users have to do research first to find the
|
||||
"best" available driver for a database in order to get started, we would have failed as an open source community.
|
||||
|
||||
#### Can I mix multiple sources during a batch of migrations?
|
||||
No.
|
||||
|
||||
#### What does "dirty" database mean?
|
||||
Before a migration runs, each database sets a dirty flag. Execution stops if a migration fails and the dirty state persists,
|
||||
which prevents attempts to run more migrations on top of a failed migration. You need to manually fix the error
|
||||
and then "force" the expected version.
|
||||
|
||||
#### What happens if two programs try and update the database at the same time?
|
||||
Database-specific locking features are used by *some* database drivers to prevent multiple instances of migrate from running migrations at the same time
|
||||
the same database at the same time. For example, the MySQL driver uses the `GET_LOCK` function, while the Postgres driver uses
|
||||
the `pg_advisory_lock` function.
|
||||
|
||||
#### Do I need to create a table for tracking migration version used?
|
||||
No, it is done automatically.
|
||||
|
||||
#### Can I use migrate with a non-Go project?
|
||||
Yes, you can use the migrate CLI in a non-Go project, but there are probably other libraries/frameworks available that offer better test and deploy integrations in that language/framework.
|
||||
|
||||
#### I have got an error `Dirty database version 1. Fix and force version`. What should I do?
|
||||
Keep calm and refer to [the getting started docs](GETTING_STARTED.md#forcing-your-database-version).
|
||||
53
vendor/github.com/golang-migrate/migrate/v4/GETTING_STARTED.md
сгенерированный
поставляемый
53
vendor/github.com/golang-migrate/migrate/v4/GETTING_STARTED.md
сгенерированный
поставляемый
@@ -1,53 +0,0 @@
|
||||
# Getting started
|
||||
Before you start, you should understand the concept of forward/up and reverse/down database migrations.
|
||||
|
||||
Configure a database for your application. Make sure that your database driver is supported [here](README.md#databases)
|
||||
|
||||
## Create migrations
|
||||
Create some migrations using migrate CLI. Here is an example:
|
||||
```
|
||||
migrate create -ext sql -dir db/migrations -seq create_users_table
|
||||
```
|
||||
Once you create your files, you should fill them.
|
||||
|
||||
**IMPORTANT:** In a project developed by more than one person there is a chance of migrations inconsistency - e.g. two developers can create conflicting migrations, and the developer that created his migration later gets it merged to the repository first.
|
||||
Developers and Teams should keep an eye on such cases (especially during code review).
|
||||
[Here](https://github.com/golang-migrate/migrate/issues/179#issuecomment-475821264) is the issue summary if you would like to read more.
|
||||
|
||||
Consider making your migrations idempotent - we can run the same sql code twice in a row with the same result. This makes our migrations more robust. On the other hand, it causes slightly less control over database schema - e.g. let's say you forgot to drop the table in down migration. You run down migration - the table is still there. When you run up migration again - `CREATE TABLE` would return an error, helping you find an issue in down migration, while `CREATE TABLE IF NOT EXISTS` would not. Use those conditions wisely.
|
||||
|
||||
In case you would like to run several commands/queries in one migration, you should wrap them in a transaction (if your database supports it).
|
||||
This way if one of commands fails, our database will remain unchanged.
|
||||
|
||||
## Run migrations
|
||||
Run your migrations through the CLI or your app and check if they applied expected changes.
|
||||
Just to give you an idea:
|
||||
```
|
||||
migrate -database YOUR_DATABASE_URL -path PATH_TO_YOUR_MIGRATIONS up
|
||||
```
|
||||
|
||||
Just add the code to your app and you're ready to go!
|
||||
|
||||
Before commiting your migrations you should run your migrations up, down, and then up again to see if migrations are working properly both ways.
|
||||
(e.g. if you created a table in a migration but reverse migration did not delete it, you will encounter an error when running the forward migration again)
|
||||
It's also worth checking your migrations in a separate, containerized environment. You can find some tools in the end of this document.
|
||||
|
||||
**IMPORTANT:** If you would like to run multiple instances of your app on different machines be sure to use a database that supports locking when running migrations. Otherwise you may encounter issues.
|
||||
|
||||
## Forcing your database version
|
||||
In case you run a migration that contained an error, migrate will not let you run other migrations on the same database. You will see an error like `Dirty database version 1. Fix and force version`, even when you fix the erred migration. This means your database was marked as 'dirty'.
|
||||
You need to investigate the migration error - was your migration applied partially, or was it not applied at all? Once you know, you should force your database to a version reflecting it's real state. You can do so with `force` command:
|
||||
```
|
||||
migrate -path PATH_TO_YOUR_MIGRATIONS -database YOUR_DATABASE_URL force VERSION
|
||||
```
|
||||
Once you force the version and your migration was fixed, your database is 'clean' again and you can proceed with your migrations.
|
||||
|
||||
For details and example of usage see [this comment](https://github.com/golang-migrate/migrate/issues/282#issuecomment-530743258).
|
||||
|
||||
## Further reading:
|
||||
- [PostgreSQL tutorial](database/postgres/TUTORIAL.md)
|
||||
- [Best practices](MIGRATIONS.md)
|
||||
- [FAQ](FAQ.md)
|
||||
- Tools for testing your migrations in a container:
|
||||
- https://github.com/dhui/dktest
|
||||
- https://github.com/ory/dockertest
|
||||
86
vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md
сгенерированный
поставляемый
86
vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md
сгенерированный
поставляемый
@@ -1,86 +0,0 @@
|
||||
# Migrations
|
||||
|
||||
## Migration Filename Format
|
||||
|
||||
A single logical migration is represented as two separate migration files, one
|
||||
to migrate "up" to the specified version from the previous version, and a second
|
||||
to migrate back "down" to the previous version. These migrations can be provided
|
||||
by any one of the supported [migration sources](./README.md#migration-sources).
|
||||
|
||||
The ordering and direction of the migration files is determined by the filenames
|
||||
used for them. `migrate` expects the filenames of migrations to have the format:
|
||||
|
||||
{version}_{title}.up.{extension}
|
||||
{version}_{title}.down.{extension}
|
||||
|
||||
The `title` of each migration is unused, and is only for readability. Similarly,
|
||||
the `extension` of the migration files is not checked by the library, and should
|
||||
be an appropriate format for the database in use (`.sql` for SQL variants, for
|
||||
instance).
|
||||
|
||||
Versions of migrations may be represented as any 64 bit unsigned integer.
|
||||
All migrations are applied upward in order of increasing version number, and
|
||||
downward by decreasing version number.
|
||||
|
||||
Common versioning schemes include incrementing integers:
|
||||
|
||||
1_initialize_schema.down.sql
|
||||
1_initialize_schema.up.sql
|
||||
2_add_table.down.sql
|
||||
2_add_table.up.sql
|
||||
...
|
||||
|
||||
Or timestamps at an appropriate resolution:
|
||||
|
||||
1500360784_initialize_schema.down.sql
|
||||
1500360784_initialize_schema.up.sql
|
||||
1500445949_add_table.down.sql
|
||||
1500445949_add_table.up.sql
|
||||
...
|
||||
|
||||
But any scheme resulting in distinct, incrementing integers as versions is valid.
|
||||
|
||||
It is suggested that the version number of corresponding `up` and `down` migration
|
||||
files be equivalent for clarity, but they are allowed to differ so long as the
|
||||
relative ordering of the migrations is preserved.
|
||||
|
||||
The migration files are permitted to be "empty", in the event that a migration
|
||||
is a no-op or is irreversible. It is recommended to still include both migration
|
||||
files by making the whole migration file consist of a comment.
|
||||
If your database does not support comments, then deleting the migration file will also work.
|
||||
Note, an actual empty file (e.g. a 0 byte file) may cause issues with your database since migrate
|
||||
will attempt to run an empty query. In this case, deleting the migration file will also work.
|
||||
For the rational of this behavior see:
|
||||
[#244 (comment)](https://github.com/golang-migrate/migrate/issues/244#issuecomment-510758270)
|
||||
|
||||
## Migration Content Format
|
||||
|
||||
The format of the migration files themselves varies between database systems.
|
||||
Different databases have different semantics around schema changes and when and
|
||||
how they are allowed to occur
|
||||
(for instance, [if schema changes can occur within a transaction](https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis)).
|
||||
|
||||
As such, the `migrate` library has little to no checking around the format of
|
||||
migration sources. The migration files are generally processed directly by the
|
||||
drivers as raw operations.
|
||||
|
||||
## Reversibility of Migrations
|
||||
|
||||
Best practice for writing schema migration is that all migrations should be
|
||||
reversible. It should in theory be possible for run migrations down and back up
|
||||
through any and all versions with the state being fully cleaned and recreated
|
||||
by doing so.
|
||||
|
||||
By adhering to this recommended practice, development and deployment of new code
|
||||
is cleaner and easier (cleaning database state for a new feature should be as
|
||||
easy as migrating down to a prior version, and back up to the latest).
|
||||
|
||||
As opposed to some other migration libraries, `migrate` represents up and down
|
||||
migrations as separate files. This prevents any non-standard file syntax from
|
||||
being introduced which may result in unintended behavior or errors, depending
|
||||
on what database is processing the file.
|
||||
|
||||
While it is technically possible for an up or down migration to exist on its own
|
||||
without an equivalently versioned counterpart, it is strongly recommended to
|
||||
always include a down migration which cleans up the state of the corresponding
|
||||
up migration.
|
||||
120
vendor/github.com/golang-migrate/migrate/v4/Makefile
сгенерированный
поставляемый
120
vendor/github.com/golang-migrate/migrate/v4/Makefile
сгенерированный
поставляемый
@@ -1,120 +0,0 @@
|
||||
SOURCE ?= file go_bindata github github_ee bitbucket aws_s3 google_cloud_storage godoc_vfs gitlab
|
||||
DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver firebird neo4j pgx
|
||||
DATABASE_TEST ?= $(DATABASE) sqlite sqlite3 sqlcipher
|
||||
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
|
||||
TEST_FLAGS ?=
|
||||
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
|
||||
COVERAGE_DIR ?= .coverage
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build -ldflags='-X main.Version=$(VERSION)' -tags '$(DATABASE) $(SOURCE)' ./cmd/migrate
|
||||
|
||||
build-docker:
|
||||
CGO_ENABLED=0 go build -a -o build/migrate.linux-386 -ldflags="-s -w -X main.Version=${VERSION}" -tags "$(DATABASE) $(SOURCE)" ./cmd/migrate
|
||||
|
||||
build-cli: clean
|
||||
-mkdir ./cli/build
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o ../../cli/build/migrate.linux-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -a -o ../../cli/build/migrate.linux-armv7 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -a -o ../../cli/build/migrate.linux-arm64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -a -o ../../cli/build/migrate.darwin-amd64 -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -a -o ../../cli/build/migrate.windows-386.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cmd/migrate && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -a -o ../../cli/build/migrate.windows-amd64.exe -ldflags='-X main.Version=$(VERSION) -extldflags "-static"' -tags '$(DATABASE) $(SOURCE)' .
|
||||
cd ./cli/build && find . -name 'migrate*' | xargs -I{} tar czf {}.tar.gz {}
|
||||
cd ./cli/build && shasum -a 256 * > sha256sum.txt
|
||||
cat ./cli/build/sha256sum.txt
|
||||
|
||||
|
||||
clean:
|
||||
-rm -r ./cli/build
|
||||
|
||||
|
||||
test-short:
|
||||
make test-with-flags --ignore-errors TEST_FLAGS='-short'
|
||||
|
||||
|
||||
test:
|
||||
@-rm -r $(COVERAGE_DIR)
|
||||
@mkdir $(COVERAGE_DIR)
|
||||
make test-with-flags TEST_FLAGS='-v -race -covermode atomic -coverprofile $$(COVERAGE_DIR)/combined.txt -bench=. -benchmem -timeout 20m'
|
||||
|
||||
|
||||
test-with-flags:
|
||||
@echo SOURCE: $(SOURCE)
|
||||
@echo DATABASE_TEST: $(DATABASE_TEST)
|
||||
|
||||
@go test $(TEST_FLAGS) ./...
|
||||
|
||||
|
||||
kill-orphaned-docker-containers:
|
||||
docker rm -f $(shell docker ps -aq --filter label=migrate_test)
|
||||
|
||||
|
||||
html-coverage:
|
||||
go tool cover -html=$(COVERAGE_DIR)/combined.txt
|
||||
|
||||
|
||||
list-external-deps:
|
||||
$(call external_deps,'.')
|
||||
$(call external_deps,'./cli/...')
|
||||
$(call external_deps,'./testing/...')
|
||||
|
||||
$(foreach v, $(SOURCE), $(call external_deps,'./source/$(v)/...'))
|
||||
$(call external_deps,'./source/testing/...')
|
||||
$(call external_deps,'./source/stub/...')
|
||||
|
||||
$(foreach v, $(DATABASE), $(call external_deps,'./database/$(v)/...'))
|
||||
$(call external_deps,'./database/testing/...')
|
||||
$(call external_deps,'./database/stub/...')
|
||||
|
||||
|
||||
restore-import-paths:
|
||||
find . -name '*.go' -type f -execdir sed -i '' s%\"github.com/$(REPO_OWNER)/migrate%\"github.com/mattes/migrate%g '{}' \;
|
||||
|
||||
|
||||
rewrite-import-paths:
|
||||
find . -name '*.go' -type f -execdir sed -i '' s%\"github.com/mattes/migrate%\"github.com/$(REPO_OWNER)/migrate%g '{}' \;
|
||||
|
||||
|
||||
# example: fswatch -0 --exclude .godoc.pid --event Updated . | xargs -0 -n1 -I{} make docs
|
||||
docs:
|
||||
-make kill-docs
|
||||
nohup godoc -play -http=127.0.0.1:6064 </dev/null >/dev/null 2>&1 & echo $$! > .godoc.pid
|
||||
cat .godoc.pid
|
||||
|
||||
|
||||
kill-docs:
|
||||
@cat .godoc.pid
|
||||
kill -9 $$(cat .godoc.pid)
|
||||
rm .godoc.pid
|
||||
|
||||
|
||||
open-docs:
|
||||
open http://localhost:6064/pkg/github.com/$(REPO_OWNER)/migrate
|
||||
|
||||
|
||||
# example: make release V=0.0.0
|
||||
release:
|
||||
git tag v$(V)
|
||||
@read -p "Press enter to confirm and push to origin ..." && git push origin v$(V)
|
||||
|
||||
echo-source:
|
||||
@echo "$(SOURCE)"
|
||||
|
||||
echo-database:
|
||||
@echo "$(DATABASE)"
|
||||
|
||||
|
||||
define external_deps
|
||||
@echo '-- $(1)'; go list -f '{{join .Deps "\n"}}' $(1) | grep -v github.com/$(REPO_OWNER)/migrate | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
|
||||
|
||||
endef
|
||||
|
||||
|
||||
.PHONY: build build-docker build-cli clean test-short test test-with-flags html-coverage \
|
||||
restore-import-paths rewrite-import-paths list-external-deps release \
|
||||
docs kill-docs open-docs kill-orphaned-docker-containers echo-source echo-database
|
||||
|
||||
SHELL = /bin/sh
|
||||
RAND = $(shell echo $$RANDOM)
|
||||
|
||||
186
vendor/github.com/golang-migrate/migrate/v4/README.md
сгенерированный
поставляемый
186
vendor/github.com/golang-migrate/migrate/v4/README.md
сгенерированный
поставляемый
@@ -1,186 +0,0 @@
|
||||
[](https://github.com/golang-migrate/migrate/actions/workflows/ci.yaml?query=branch%3Amaster)
|
||||
[](https://pkg.go.dev/github.com/golang-migrate/migrate/v4)
|
||||
[](https://coveralls.io/github/golang-migrate/migrate?branch=master)
|
||||
[](https://packagecloud.io/golang-migrate/migrate?filter=debs)
|
||||
[](https://hub.docker.com/r/migrate/migrate/)
|
||||

|
||||
[](https://github.com/golang-migrate/migrate/releases)
|
||||
[](https://goreportcard.com/report/github.com/golang-migrate/migrate)
|
||||
|
||||
# migrate
|
||||
|
||||
__Database migrations written in Go. Use as [CLI](#cli-usage) or import as [library](#use-in-your-go-project).__
|
||||
|
||||
* Migrate reads migrations from [sources](#migration-sources)
|
||||
and applies them in correct order to a [database](#databases).
|
||||
* Drivers are "dumb", migrate glues everything together and makes sure the logic is bulletproof.
|
||||
(Keeps the drivers lightweight, too.)
|
||||
* Database drivers don't assume things or try to correct user input. When in doubt, fail.
|
||||
|
||||
Forked from [mattes/migrate](https://github.com/mattes/migrate)
|
||||
|
||||
## Databases
|
||||
|
||||
Database drivers run migrations. [Add a new database?](database/driver.go)
|
||||
|
||||
* [PostgreSQL](database/postgres)
|
||||
* [PGX](database/pgx)
|
||||
* [Redshift](database/redshift)
|
||||
* [Ql](database/ql)
|
||||
* [Cassandra](database/cassandra)
|
||||
* [SQLite](database/sqlite)
|
||||
* [SQLite3](database/sqlite3) ([todo #165](https://github.com/mattes/migrate/issues/165))
|
||||
* [SQLCipher](database/sqlcipher)
|
||||
* [MySQL/ MariaDB](database/mysql)
|
||||
* [Neo4j](database/neo4j)
|
||||
* [MongoDB](database/mongodb)
|
||||
* [CrateDB](database/crate) ([todo #170](https://github.com/mattes/migrate/issues/170))
|
||||
* [Shell](database/shell) ([todo #171](https://github.com/mattes/migrate/issues/171))
|
||||
* [Google Cloud Spanner](database/spanner)
|
||||
* [CockroachDB](database/cockroachdb)
|
||||
* [ClickHouse](database/clickhouse)
|
||||
* [Firebird](database/firebird)
|
||||
* [MS SQL Server](database/sqlserver)
|
||||
|
||||
### Database URLs
|
||||
|
||||
Database connection strings are specified via URLs. The URL format is driver dependent but generally has the form: `dbdriver://username:password@host:port/dbname?param1=true¶m2=false`
|
||||
|
||||
Any [reserved URL characters](https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters) need to be escaped. Note, the `%` character also [needs to be escaped](https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_the_percent_character)
|
||||
|
||||
Explicitly, the following characters need to be escaped:
|
||||
`!`, `#`, `$`, `%`, `&`, `'`, `(`, `)`, `*`, `+`, `,`, `/`, `:`, `;`, `=`, `?`, `@`, `[`, `]`
|
||||
|
||||
It's easiest to always run the URL parts of your DB connection URL (e.g. username, password, etc) through an URL encoder. See the example Python snippets below:
|
||||
|
||||
```bash
|
||||
$ python3 -c 'import urllib.parse; print(urllib.parse.quote(input("String to encode: "), ""))'
|
||||
String to encode: FAKEpassword!#$%&'()*+,/:;=?@[]
|
||||
FAKEpassword%21%23%24%25%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D
|
||||
$ python2 -c 'import urllib; print urllib.quote(raw_input("String to encode: "), "")'
|
||||
String to encode: FAKEpassword!#$%&'()*+,/:;=?@[]
|
||||
FAKEpassword%21%23%24%25%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D
|
||||
$
|
||||
```
|
||||
|
||||
## Migration Sources
|
||||
|
||||
Source drivers read migrations from local or remote sources. [Add a new source?](source/driver.go)
|
||||
|
||||
* [Filesystem](source/file) - read from filesystem
|
||||
* [Go-Bindata](source/go_bindata) - read from embedded binary data ([jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata))
|
||||
* [GitHub](source/github) - read from remote GitHub repositories
|
||||
* [GitHub Enterprise](source/github_ee) - read from remote GitHub Enterprise repositories
|
||||
* [Bitbucket](source/bitbucket) - read from remote Bitbucket repositories
|
||||
* [Gitlab](source/gitlab) - read from remote Gitlab repositories
|
||||
* [AWS S3](source/aws_s3) - read from Amazon Web Services S3
|
||||
* [Google Cloud Storage](source/google_cloud_storage) - read from Google Cloud Platform Storage
|
||||
|
||||
## CLI usage
|
||||
|
||||
* Simple wrapper around this library.
|
||||
* Handles ctrl+c (SIGINT) gracefully.
|
||||
* No config search paths, no config files, no magic ENV var injections.
|
||||
|
||||
__[CLI Documentation](cmd/migrate)__
|
||||
|
||||
### Basic usage
|
||||
|
||||
```bash
|
||||
$ migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2
|
||||
```
|
||||
|
||||
### Docker usage
|
||||
|
||||
```bash
|
||||
$ docker run -v {{ migration dir }}:/migrations --network host migrate/migrate
|
||||
-path=/migrations/ -database postgres://localhost:5432/database up 2
|
||||
```
|
||||
|
||||
## Use in your Go project
|
||||
|
||||
* API is stable and frozen for this release (v3 & v4).
|
||||
* Uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies.
|
||||
* To help prevent database corruptions, it supports graceful stops via `GracefulStop chan bool`.
|
||||
* Bring your own logger.
|
||||
* Uses `io.Reader` streams internally for low memory overhead.
|
||||
* Thread-safe and no goroutine leaks.
|
||||
|
||||
__[Go Documentation](https://godoc.org/github.com/golang-migrate/migrate)__
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/github"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m, err := migrate.New(
|
||||
"github://mattes:personal-access-token@mattes/migrate_test",
|
||||
"postgres://localhost:5432/database?sslmode=enable")
|
||||
m.Steps(2)
|
||||
}
|
||||
```
|
||||
|
||||
Want to use an existing database client?
|
||||
|
||||
```go
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("postgres", "postgres://localhost:5432/database?sslmode=enable")
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
m, err := migrate.NewWithDatabaseInstance(
|
||||
"file:///migrations",
|
||||
"postgres", driver)
|
||||
m.Steps(2)
|
||||
}
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
Go to [getting started](GETTING_STARTED.md)
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [CockroachDB](database/cockroachdb/TUTORIAL.md)
|
||||
* [PostgreSQL](database/postgres/TUTORIAL.md)
|
||||
|
||||
(more tutorials to come)
|
||||
|
||||
## Migration files
|
||||
|
||||
Each migration has an up and down migration. [Why?](FAQ.md#why-two-separate-files-up-and-down-for-a-migration)
|
||||
|
||||
```bash
|
||||
1481574547_create_users_table.up.sql
|
||||
1481574547_create_users_table.down.sql
|
||||
```
|
||||
|
||||
[Best practices: How to write migrations.](MIGRATIONS.md)
|
||||
|
||||
## Versions
|
||||
|
||||
Version | Supported? | Import | Notes
|
||||
--------|------------|--------|------
|
||||
**master** | :white_check_mark: | `import "github.com/golang-migrate/migrate/v4"` | New features and bug fixes arrive here first |
|
||||
**v4** | :white_check_mark: | `import "github.com/golang-migrate/migrate/v4"` | Used for stable releases |
|
||||
**v3** | :x: | `import "github.com/golang-migrate/migrate"` (with package manager) or `import "gopkg.in/golang-migrate/migrate.v3"` (not recommended) | **DO NOT USE** - No longer supported |
|
||||
|
||||
## Development and Contributing
|
||||
|
||||
Yes, please! [`Makefile`](Makefile) is your friend,
|
||||
read the [development guide](CONTRIBUTING.md).
|
||||
|
||||
Also have a look at the [FAQ](FAQ.md).
|
||||
|
||||
---
|
||||
|
||||
Looking for alternatives? [https://awesome-go.com/#database](https://awesome-go.com/#database).
|
||||
123
vendor/github.com/golang-migrate/migrate/v4/database/driver.go
сгенерированный
поставляемый
123
vendor/github.com/golang-migrate/migrate/v4/database/driver.go
сгенерированный
поставляемый
@@ -1,123 +0,0 @@
|
||||
// Package database provides the Database interface.
|
||||
// All database drivers must implement this interface, register themselves,
|
||||
// optionally provide a `WithInstance` function and pass the tests
|
||||
// in package database/testing.
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
iurl "github.com/golang-migrate/migrate/v4/internal/url"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLocked = fmt.Errorf("can't acquire lock")
|
||||
ErrNotLocked = fmt.Errorf("can't unlock, as not currently locked")
|
||||
)
|
||||
|
||||
const NilVersion int = -1
|
||||
|
||||
var driversMu sync.RWMutex
|
||||
var drivers = make(map[string]Driver)
|
||||
|
||||
// Driver is the interface every database driver must implement.
|
||||
//
|
||||
// How to implement a database driver?
|
||||
// 1. Implement this interface.
|
||||
// 2. Optionally, add a function named `WithInstance`.
|
||||
// This function should accept an existing DB instance and a Config{} struct
|
||||
// and return a driver instance.
|
||||
// 3. Add a test that calls database/testing.go:Test()
|
||||
// 4. Add own tests for Open(), WithInstance() (when provided) and Close().
|
||||
// All other functions are tested by tests in database/testing.
|
||||
// Saves you some time and makes sure all database drivers behave the same way.
|
||||
// 5. Call Register in init().
|
||||
// 6. Create a internal/cli/build_<driver-name>.go file
|
||||
// 7. Add driver name in 'DATABASE' variable in Makefile
|
||||
//
|
||||
// Guidelines:
|
||||
// * Don't try to correct user input. Don't assume things.
|
||||
// When in doubt, return an error and explain the situation to the user.
|
||||
// * All configuration input must come from the URL string in func Open()
|
||||
// or the Config{} struct in WithInstance. Don't os.Getenv().
|
||||
type Driver interface {
|
||||
// Open returns a new driver instance configured with parameters
|
||||
// coming from the URL string. Migrate will call this function
|
||||
// only once per instance.
|
||||
Open(url string) (Driver, error)
|
||||
|
||||
// Close closes the underlying database instance managed by the driver.
|
||||
// Migrate will call this function only once per instance.
|
||||
Close() error
|
||||
|
||||
// Lock should acquire a database lock so that only one migration process
|
||||
// can run at a time. Migrate will call this function before Run is called.
|
||||
// If the implementation can't provide this functionality, return nil.
|
||||
// Return database.ErrLocked if database is already locked.
|
||||
Lock() error
|
||||
|
||||
// Unlock should release the lock. Migrate will call this function after
|
||||
// all migrations have been run.
|
||||
Unlock() error
|
||||
|
||||
// Run applies a migration to the database. migration is guaranteed to be not nil.
|
||||
Run(migration io.Reader) error
|
||||
|
||||
// SetVersion saves version and dirty state.
|
||||
// Migrate will call this function before and after each call to Run.
|
||||
// version must be >= -1. -1 means NilVersion.
|
||||
SetVersion(version int, dirty bool) error
|
||||
|
||||
// Version returns the currently active version and if the database is dirty.
|
||||
// When no migration has been applied, it must return version -1.
|
||||
// Dirty means, a previous migration failed and user interaction is required.
|
||||
Version() (version int, dirty bool, err error)
|
||||
|
||||
// Drop deletes everything in the database.
|
||||
// Note that this is a breaking action, a new call to Open() is necessary to
|
||||
// ensure subsequent calls work as expected.
|
||||
Drop() error
|
||||
}
|
||||
|
||||
// Open returns a new driver instance.
|
||||
func Open(url string) (Driver, error) {
|
||||
scheme, err := iurl.SchemeFromURL(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driversMu.RLock()
|
||||
d, ok := drivers[scheme]
|
||||
driversMu.RUnlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("database driver: unknown driver %v (forgotten import?)", scheme)
|
||||
}
|
||||
|
||||
return d.Open(url)
|
||||
}
|
||||
|
||||
// Register globally registers a driver.
|
||||
func Register(name string, driver Driver) {
|
||||
driversMu.Lock()
|
||||
defer driversMu.Unlock()
|
||||
if driver == nil {
|
||||
panic("Register driver is nil")
|
||||
}
|
||||
if _, dup := drivers[name]; dup {
|
||||
panic("Register called twice for driver " + name)
|
||||
}
|
||||
drivers[name] = driver
|
||||
}
|
||||
|
||||
// List lists the registered drivers
|
||||
func List() []string {
|
||||
driversMu.RLock()
|
||||
defer driversMu.RUnlock()
|
||||
names := make([]string, 0, len(drivers))
|
||||
for n := range drivers {
|
||||
names = append(names, n)
|
||||
}
|
||||
return names
|
||||
}
|
||||
27
vendor/github.com/golang-migrate/migrate/v4/database/error.go
сгенерированный
поставляемый
27
vendor/github.com/golang-migrate/migrate/v4/database/error.go
сгенерированный
поставляемый
@@ -1,27 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error should be used for errors involving queries ran against the database
|
||||
type Error struct {
|
||||
// Optional: the line number
|
||||
Line uint
|
||||
|
||||
// Query is a query excerpt
|
||||
Query []byte
|
||||
|
||||
// Err is a useful/helping error message for humans
|
||||
Err string
|
||||
|
||||
// OrigErr is the underlying error
|
||||
OrigErr error
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if len(e.Err) == 0 {
|
||||
return fmt.Sprintf("%v in line %v: %s", e.OrigErr, e.Line, e.Query)
|
||||
}
|
||||
return fmt.Sprintf("%v in line %v: %s (details: %v)", e.Err, e.Line, e.Query, e.OrigErr)
|
||||
}
|
||||
46
vendor/github.com/golang-migrate/migrate/v4/database/multistmt/parse.go
сгенерированный
поставляемый
46
vendor/github.com/golang-migrate/migrate/v4/database/multistmt/parse.go
сгенерированный
поставляемый
@@ -1,46 +0,0 @@
|
||||
// Package multistmt provides methods for parsing multi-statement database migrations
|
||||
package multistmt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// StartBufSize is the default starting size of the buffer used to scan and parse multi-statement migrations
|
||||
var StartBufSize = 4096
|
||||
|
||||
// Handler handles a single migration parsed from a multi-statement migration.
|
||||
// It's given the single migration to handle and returns whether or not further statements
|
||||
// from the multi-statement migration should be parsed and handled.
|
||||
type Handler func(migration []byte) bool
|
||||
|
||||
func splitWithDelimiter(delimiter []byte) func(d []byte, atEOF bool) (int, []byte, error) {
|
||||
return func(d []byte, atEOF bool) (int, []byte, error) {
|
||||
// SplitFunc inspired by bufio.ScanLines() implementation
|
||||
if atEOF {
|
||||
if len(d) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
return len(d), d, nil
|
||||
}
|
||||
if i := bytes.Index(d, delimiter); i >= 0 {
|
||||
return i + len(delimiter), d[:i+len(delimiter)], nil
|
||||
}
|
||||
return 0, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses the given multi-statement migration
|
||||
func Parse(reader io.Reader, delimiter []byte, maxMigrationSize int, h Handler) error {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
scanner.Buffer(make([]byte, 0, StartBufSize), maxMigrationSize)
|
||||
scanner.Split(splitWithDelimiter(delimiter))
|
||||
for scanner.Scan() {
|
||||
cont := h(scanner.Bytes())
|
||||
if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
55
vendor/github.com/golang-migrate/migrate/v4/database/mysql/README.md
сгенерированный
поставляемый
55
vendor/github.com/golang-migrate/migrate/v4/database/mysql/README.md
сгенерированный
поставляемый
@@ -1,55 +0,0 @@
|
||||
# MySQL
|
||||
|
||||
`mysql://user:password@tcp(host:port)/dbname?query`
|
||||
|
||||
| URL Query | WithInstance Config | Description |
|
||||
|------------|---------------------|-------------|
|
||||
| `x-migrations-table` | `MigrationsTable` | Name of the migrations table |
|
||||
| `x-no-lock` | `NoLock` | Set to `true` to skip `GET_LOCK`/`RELEASE_LOCK` statements. Useful for [multi-master MySQL flavors](https://www.percona.com/doc/percona-xtradb-cluster/LATEST/features/pxc-strict-mode.html#explicit-table-locking). Only run migrations from one host when this is enabled. |
|
||||
| `dbname` | `DatabaseName` | The name of the database to connect to |
|
||||
| `user` | | The user to sign in as |
|
||||
| `password` | | The user's password |
|
||||
| `host` | | The host to connect to. |
|
||||
| `port` | | The port to bind to. |
|
||||
| `tls` | | TLS / SSL encrypted connection parameter; see [go-sql-driver](https://github.com/go-sql-driver/mysql#tls). Use any name (e.g. `migrate`) if you want to use a custom TLS config (`x-tls-` queries). |
|
||||
| `x-tls-ca` | | The location of the CA (certificate authority) file. |
|
||||
| `x-tls-cert` | | The location of the client certicicate file. Must be used with `x-tls-key`. |
|
||||
| `x-tls-key` | | The location of the private key file. Must be used with `x-tls-cert`. |
|
||||
| `x-tls-insecure-skip-verify` | | Whether or not to use SSL (true\|false) |
|
||||
|
||||
## Use with existing client
|
||||
|
||||
If you use the MySQL driver with existing database client, you must create the client with parameter `multiStatements=true`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/golang-migrate/migrate"
|
||||
"github.com/golang-migrate/migrate/database/mysql"
|
||||
_ "github.com/golang-migrate/migrate/source/file"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, _ := sql.Open("mysql", "user:password@tcp(host:port)/dbname?multiStatements=true")
|
||||
driver, _ := mysql.WithInstance(db, &mysql.Config{})
|
||||
m, _ := migrate.NewWithDatabaseInstance(
|
||||
"file:///migrations",
|
||||
"mysql",
|
||||
driver,
|
||||
)
|
||||
|
||||
m.Steps(2)
|
||||
}
|
||||
```
|
||||
|
||||
## Upgrading from v1
|
||||
|
||||
1. Write down the current migration version from schema_migrations
|
||||
1. `DROP TABLE schema_migrations`
|
||||
2. Wrap your existing migrations in transactions ([BEGIN/COMMIT](https://dev.mysql.com/doc/refman/5.7/en/commit.html)) if you use multiple statements within one migration.
|
||||
3. Download and install the latest migrate version.
|
||||
4. Force the current migration version with `migrate force <current_version>`.
|
||||
494
vendor/github.com/golang-migrate/migrate/v4/database/mysql/mysql.go
сгенерированный
поставляемый
494
vendor/github.com/golang-migrate/migrate/v4/database/mysql/mysql.go
сгенерированный
поставляемый
@@ -1,494 +0,0 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"go.uber.org/atomic"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
nurl "net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/golang-migrate/migrate/v4/database"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
var _ database.Driver = (*Mysql)(nil) // explicit compile time type check
|
||||
|
||||
func init() {
|
||||
database.Register("mysql", &Mysql{})
|
||||
}
|
||||
|
||||
var DefaultMigrationsTable = "schema_migrations"
|
||||
|
||||
var (
|
||||
ErrDatabaseDirty = fmt.Errorf("database is dirty")
|
||||
ErrNilConfig = fmt.Errorf("no config")
|
||||
ErrNoDatabaseName = fmt.Errorf("no database name")
|
||||
ErrAppendPEM = fmt.Errorf("failed to append PEM")
|
||||
ErrTLSCertKeyConfig = fmt.Errorf("To use TLS client authentication, both x-tls-cert and x-tls-key must not be empty")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MigrationsTable string
|
||||
DatabaseName string
|
||||
NoLock bool
|
||||
}
|
||||
|
||||
type Mysql struct {
|
||||
// mysql RELEASE_LOCK must be called from the same conn, so
|
||||
// just do everything over a single conn anyway.
|
||||
conn *sql.Conn
|
||||
db *sql.DB
|
||||
isLocked atomic.Bool
|
||||
|
||||
config *Config
|
||||
}
|
||||
|
||||
// connection instance must have `multiStatements` set to true
|
||||
func WithConnection(ctx context.Context, conn *sql.Conn, config *Config) (*Mysql, error) {
|
||||
if config == nil {
|
||||
return nil, ErrNilConfig
|
||||
}
|
||||
|
||||
if err := conn.PingContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mx := &Mysql{
|
||||
conn: conn,
|
||||
db: nil,
|
||||
config: config,
|
||||
}
|
||||
|
||||
if config.DatabaseName == "" {
|
||||
query := `SELECT DATABASE()`
|
||||
var databaseName sql.NullString
|
||||
if err := conn.QueryRowContext(ctx, query).Scan(&databaseName); err != nil {
|
||||
return nil, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if len(databaseName.String) == 0 {
|
||||
return nil, ErrNoDatabaseName
|
||||
}
|
||||
|
||||
config.DatabaseName = databaseName.String
|
||||
}
|
||||
|
||||
if len(config.MigrationsTable) == 0 {
|
||||
config.MigrationsTable = DefaultMigrationsTable
|
||||
}
|
||||
|
||||
if err := mx.ensureVersionTable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mx, nil
|
||||
}
|
||||
|
||||
// instance must have `multiStatements` set to true
|
||||
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
if err := instance.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := instance.Conn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mx, err := WithConnection(ctx, conn, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mx.db = instance
|
||||
|
||||
return mx, nil
|
||||
}
|
||||
|
||||
// extractCustomQueryParams extracts the custom query params (ones that start with "x-") from
|
||||
// mysql.Config.Params (connection parameters) as to not interfere with connecting to MySQL
|
||||
func extractCustomQueryParams(c *mysql.Config) (map[string]string, error) {
|
||||
if c == nil {
|
||||
return nil, ErrNilConfig
|
||||
}
|
||||
customQueryParams := map[string]string{}
|
||||
|
||||
for k, v := range c.Params {
|
||||
if strings.HasPrefix(k, "x-") {
|
||||
customQueryParams[k] = v
|
||||
delete(c.Params, k)
|
||||
}
|
||||
}
|
||||
return customQueryParams, nil
|
||||
}
|
||||
|
||||
func urlToMySQLConfig(url string) (*mysql.Config, error) {
|
||||
// Need to parse out custom TLS parameters and call
|
||||
// mysql.RegisterTLSConfig() before mysql.ParseDSN() is called
|
||||
// which consumes the registered tls.Config
|
||||
// Fixes: https://github.com/golang-migrate/migrate/issues/411
|
||||
//
|
||||
// Can't use url.Parse() since it fails to parse MySQL DSNs
|
||||
// mysql.ParseDSN() also searches for "?" to find query parameters:
|
||||
// https://github.com/go-sql-driver/mysql/blob/46351a8/dsn.go#L344
|
||||
if idx := strings.LastIndex(url, "?"); idx > 0 {
|
||||
rawParams := url[idx+1:]
|
||||
parsedParams, err := nurl.ParseQuery(rawParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctls := parsedParams.Get("tls")
|
||||
if len(ctls) > 0 {
|
||||
if _, isBool := readBool(ctls); !isBool && strings.ToLower(ctls) != "skip-verify" {
|
||||
rootCertPool := x509.NewCertPool()
|
||||
pem, err := ioutil.ReadFile(parsedParams.Get("x-tls-ca"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
|
||||
return nil, ErrAppendPEM
|
||||
}
|
||||
|
||||
clientCert := make([]tls.Certificate, 0, 1)
|
||||
if ccert, ckey := parsedParams.Get("x-tls-cert"), parsedParams.Get("x-tls-key"); ccert != "" || ckey != "" {
|
||||
if ccert == "" || ckey == "" {
|
||||
return nil, ErrTLSCertKeyConfig
|
||||
}
|
||||
certs, err := tls.LoadX509KeyPair(ccert, ckey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientCert = append(clientCert, certs)
|
||||
}
|
||||
|
||||
insecureSkipVerify := false
|
||||
insecureSkipVerifyStr := parsedParams.Get("x-tls-insecure-skip-verify")
|
||||
if len(insecureSkipVerifyStr) > 0 {
|
||||
x, err := strconv.ParseBool(insecureSkipVerifyStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
insecureSkipVerify = x
|
||||
}
|
||||
|
||||
err = mysql.RegisterTLSConfig(ctls, &tls.Config{
|
||||
RootCAs: rootCertPool,
|
||||
Certificates: clientCert,
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config, err := mysql.ParseDSN(strings.TrimPrefix(url, "mysql://"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.MultiStatements = true
|
||||
|
||||
// Keep backwards compatibility from when we used net/url.Parse() to parse the DSN.
|
||||
// net/url.Parse() would automatically unescape it for us.
|
||||
// See: https://play.golang.org/p/q9j1io-YICQ
|
||||
user, err := nurl.QueryUnescape(config.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.User = user
|
||||
|
||||
password, err := nurl.QueryUnescape(config.Passwd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Passwd = password
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (m *Mysql) Open(url string) (database.Driver, error) {
|
||||
config, err := urlToMySQLConfig(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
customParams, err := extractCustomQueryParams(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
noLockParam, noLock := customParams["x-no-lock"], false
|
||||
if noLockParam != "" {
|
||||
noLock, err = strconv.ParseBool(noLockParam)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse x-no-lock as bool: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", config.FormatDSN())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mx, err := WithInstance(db, &Config{
|
||||
DatabaseName: config.DBName,
|
||||
MigrationsTable: customParams["x-migrations-table"],
|
||||
NoLock: noLock,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mx, nil
|
||||
}
|
||||
|
||||
func (m *Mysql) Close() error {
|
||||
connErr := m.conn.Close()
|
||||
var dbErr error
|
||||
if m.db != nil {
|
||||
dbErr = m.db.Close()
|
||||
}
|
||||
|
||||
if connErr != nil || dbErr != nil {
|
||||
return fmt.Errorf("conn: %v, db: %v", connErr, dbErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mysql) Lock() error {
|
||||
return database.CasRestoreOnErr(&m.isLocked, false, true, database.ErrLocked, func() error {
|
||||
if m.config.NoLock {
|
||||
return nil
|
||||
}
|
||||
aid, err := database.GenerateAdvisoryLockId(
|
||||
fmt.Sprintf("%s:%s", m.config.DatabaseName, m.config.MigrationsTable))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := "SELECT GET_LOCK(?, 10)"
|
||||
var success bool
|
||||
if err := m.conn.QueryRowContext(context.Background(), query, aid).Scan(&success); err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
|
||||
}
|
||||
|
||||
if !success {
|
||||
return database.ErrLocked
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Mysql) Unlock() error {
|
||||
return database.CasRestoreOnErr(&m.isLocked, true, false, database.ErrNotLocked, func() error {
|
||||
if m.config.NoLock {
|
||||
return nil
|
||||
}
|
||||
|
||||
aid, err := database.GenerateAdvisoryLockId(
|
||||
fmt.Sprintf("%s:%s", m.config.DatabaseName, m.config.MigrationsTable))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `SELECT RELEASE_LOCK(?)`
|
||||
if _, err := m.conn.ExecContext(context.Background(), query, aid); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
// NOTE: RELEASE_LOCK could return NULL or (or 0 if the code is changed),
|
||||
// in which case isLocked should be true until the timeout expires -- synchronizing
|
||||
// these states is likely not worth trying to do; reconsider the necessity of isLocked.
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Mysql) Run(migration io.Reader) error {
|
||||
migr, err := ioutil.ReadAll(migration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := string(migr[:])
|
||||
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return database.Error{OrigErr: err, Err: "migration failed", Query: migr}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mysql) SetVersion(version int, dirty bool) error {
|
||||
tx, err := m.conn.BeginTx(context.Background(), &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "transaction start failed"}
|
||||
}
|
||||
|
||||
query := "TRUNCATE `" + m.config.MigrationsTable + "`"
|
||||
if _, err := tx.ExecContext(context.Background(), query); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
// Also re-write the schema version for nil dirty versions to prevent
|
||||
// empty schema version for failed down migration on the first migration
|
||||
// See: https://github.com/golang-migrate/migrate/issues/330
|
||||
if version >= 0 || (version == database.NilVersion && dirty) {
|
||||
query := "INSERT INTO `" + m.config.MigrationsTable + "` (version, dirty) VALUES (?, ?)"
|
||||
if _, err := tx.ExecContext(context.Background(), query, version, dirty); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mysql) Version() (version int, dirty bool, err error) {
|
||||
query := "SELECT version, dirty FROM `" + m.config.MigrationsTable + "` LIMIT 1"
|
||||
err = m.conn.QueryRowContext(context.Background(), query).Scan(&version, &dirty)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return database.NilVersion, false, nil
|
||||
|
||||
case err != nil:
|
||||
if e, ok := err.(*mysql.MySQLError); ok {
|
||||
if e.Number == 0 {
|
||||
return database.NilVersion, false, nil
|
||||
}
|
||||
}
|
||||
return 0, false, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
|
||||
default:
|
||||
return version, dirty, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mysql) Drop() (err error) {
|
||||
// select all tables
|
||||
query := `SHOW TABLES LIKE '%'`
|
||||
tables, err := m.conn.QueryContext(context.Background(), query)
|
||||
if err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
defer func() {
|
||||
if errClose := tables.Close(); errClose != nil {
|
||||
err = multierror.Append(err, errClose)
|
||||
}
|
||||
}()
|
||||
|
||||
// delete one table after another
|
||||
tableNames := make([]string, 0)
|
||||
for tables.Next() {
|
||||
var tableName string
|
||||
if err := tables.Scan(&tableName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tableName) > 0 {
|
||||
tableNames = append(tableNames, tableName)
|
||||
}
|
||||
}
|
||||
if err := tables.Err(); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if len(tableNames) > 0 {
|
||||
// disable checking foreign key constraints until finished
|
||||
query = `SET foreign_key_checks = 0`
|
||||
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// enable foreign key checks
|
||||
_, _ = m.conn.ExecContext(context.Background(), `SET foreign_key_checks = 1`)
|
||||
}()
|
||||
|
||||
// delete one by one ...
|
||||
for _, t := range tableNames {
|
||||
query = "DROP TABLE IF EXISTS `" + t + "`"
|
||||
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureVersionTable checks if versions table exists and, if not, creates it.
|
||||
// Note that this function locks the database, which deviates from the usual
|
||||
// convention of "caller locks" in the Mysql type.
|
||||
func (m *Mysql) ensureVersionTable() (err error) {
|
||||
if err = m.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := m.Unlock(); e != nil {
|
||||
if err == nil {
|
||||
err = e
|
||||
} else {
|
||||
err = multierror.Append(err, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// check if migration table exists
|
||||
var result string
|
||||
query := `SHOW TABLES LIKE '` + m.config.MigrationsTable + `'`
|
||||
if err := m.conn.QueryRowContext(context.Background(), query).Scan(&result); err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if not, create the empty migration table
|
||||
query = "CREATE TABLE `" + m.config.MigrationsTable + "` (version bigint not null primary key, dirty boolean not null)"
|
||||
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the bool value of the input.
|
||||
// The 2nd return value indicates if the input was a valid bool value
|
||||
// See https://github.com/go-sql-driver/mysql/blob/a059889267dc7170331388008528b3b44479bffb/utils.go#L71
|
||||
func readBool(input string) (value bool, valid bool) {
|
||||
switch input {
|
||||
case "1", "true", "TRUE", "True":
|
||||
return true, true
|
||||
case "0", "false", "FALSE", "False":
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Not a valid bool value
|
||||
return
|
||||
}
|
||||
39
vendor/github.com/golang-migrate/migrate/v4/database/postgres/README.md
сгенерированный
поставляемый
39
vendor/github.com/golang-migrate/migrate/v4/database/postgres/README.md
сгенерированный
поставляемый
@@ -1,39 +0,0 @@
|
||||
# postgres
|
||||
|
||||
`postgres://user:password@host:port/dbname?query` (`postgresql://` works, too)
|
||||
|
||||
| URL Query | WithInstance Config | Description |
|
||||
|------------|---------------------|-------------|
|
||||
| `x-migrations-table` | `MigrationsTable` | Name of the migrations table |
|
||||
| `x-migrations-table-quoted` | `MigrationsTableQuoted` | By default, migrate quotes the migration table for SQL injection safety reasons. This option disable quoting and naively checks that you have quoted the migration table name. e.g. `"my_schema"."schema_migrations"` |
|
||||
| `x-statement-timeout` | `StatementTimeout` | Abort any statement that takes more than the specified number of milliseconds |
|
||||
| `x-multi-statement` | `MultiStatementEnabled` | Enable multi-statement execution (default: false) |
|
||||
| `x-multi-statement-max-size` | `MultiStatementMaxSize` | Maximum size of single statement in bytes (default: 10MB) |
|
||||
| `dbname` | `DatabaseName` | The name of the database to connect to |
|
||||
| `search_path` | | This variable specifies the order in which schemas are searched when an object is referenced by a simple name with no schema specified. |
|
||||
| `user` | | The user to sign in as |
|
||||
| `password` | | The user's password |
|
||||
| `host` | | The host to connect to. Values that start with / are for unix domain sockets. (default is localhost) |
|
||||
| `port` | | The port to bind to. (default is 5432) |
|
||||
| `fallback_application_name` | | An application_name to fall back to if one isn't provided. |
|
||||
| `connect_timeout` | | Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. |
|
||||
| `sslcert` | | Cert file location. The file must contain PEM encoded data. |
|
||||
| `sslkey` | | Key file location. The file must contain PEM encoded data. |
|
||||
| `sslrootcert` | | The location of the root certificate file. The file must contain PEM encoded data. |
|
||||
| `sslmode` | | Whether or not to use SSL (disable\|require\|verify-ca\|verify-full) |
|
||||
|
||||
|
||||
## Upgrading from v1
|
||||
|
||||
1. Write down the current migration version from schema_migrations
|
||||
1. `DROP TABLE schema_migrations`
|
||||
2. Wrap your existing migrations in transactions ([BEGIN/COMMIT](https://www.postgresql.org/docs/current/static/transaction-iso.html)) if you use multiple statements within one migration.
|
||||
3. Download and install the latest migrate version.
|
||||
4. Force the current migration version with `migrate force <current_version>`.
|
||||
|
||||
## Multi-statement mode
|
||||
|
||||
In PostgreSQL running multiple SQL statements in one `Exec` executes them inside a transaction. Sometimes this
|
||||
behavior is not desirable because some statements can be only run outside of transaction (e.g.
|
||||
`CREATE INDEX CONCURRENTLY`). If you want to use `CREATE INDEX CONCURRENTLY` without activating multi-statement mode
|
||||
you have to put such statements in a separate migration files.
|
||||
167
vendor/github.com/golang-migrate/migrate/v4/database/postgres/TUTORIAL.md
сгенерированный
поставляемый
167
vendor/github.com/golang-migrate/migrate/v4/database/postgres/TUTORIAL.md
сгенерированный
поставляемый
@@ -1,167 +0,0 @@
|
||||
# PostgreSQL tutorial for beginners
|
||||
|
||||
## Create/configure database
|
||||
|
||||
For the purpose of this tutorial let's create PostgreSQL database called `example`.
|
||||
Our user here is `postgres`, password `password`, and host is `localhost`.
|
||||
```
|
||||
psql -h localhost -U postgres -w -c "create database example;"
|
||||
```
|
||||
When using Migrate CLI we need to pass to database URL. Let's export it to a variable for convenience:
|
||||
```
|
||||
export POSTGRESQL_URL='postgres://postgres:password@localhost:5432/example?sslmode=disable'
|
||||
```
|
||||
`sslmode=disable` means that the connection with our database will not be encrypted. Enabling it is left as an exercise.
|
||||
|
||||
You can find further description of database URLs [here](README.md#database-urls).
|
||||
|
||||
## Create migrations
|
||||
Let's create table called `users`:
|
||||
```
|
||||
migrate create -ext sql -dir db/migrations -seq create_users_table
|
||||
```
|
||||
If there were no errors, we should have two files available under `db/migrations` folder:
|
||||
- 000001_create_users_table.down.sql
|
||||
- 000001_create_users_table.up.sql
|
||||
|
||||
Note the `sql` extension that we provided.
|
||||
|
||||
In the `.up.sql` file let's create the table:
|
||||
```
|
||||
CREATE TABLE IF NOT EXISTS users(
|
||||
user_id serial PRIMARY KEY,
|
||||
username VARCHAR (50) UNIQUE NOT NULL,
|
||||
password VARCHAR (50) NOT NULL,
|
||||
email VARCHAR (300) UNIQUE NOT NULL
|
||||
);
|
||||
```
|
||||
And in the `.down.sql` let's delete it:
|
||||
```
|
||||
DROP TABLE IF EXISTS users;
|
||||
```
|
||||
By adding `IF EXISTS/IF NOT EXISTS` we are making migrations idempotent - you can read more about idempotency in [getting started](../../GETTING_STARTED.md#create-migrations)
|
||||
|
||||
## Run migrations
|
||||
```
|
||||
migrate -database ${POSTGRESQL_URL} -path db/migrations up
|
||||
```
|
||||
Let's check if the table was created properly by running `psql example -c "\d users"`.
|
||||
The output you are supposed to see:
|
||||
```
|
||||
Table "public.users"
|
||||
Column | Type | Modifiers
|
||||
----------+------------------------+---------------------------------------------------------
|
||||
user_id | integer | not null default nextval('users_user_id_seq'::regclass)
|
||||
username | character varying(50) | not null
|
||||
password | character varying(50) | not null
|
||||
email | character varying(300) | not null
|
||||
Indexes:
|
||||
"users_pkey" PRIMARY KEY, btree (user_id)
|
||||
"users_email_key" UNIQUE CONSTRAINT, btree (email)
|
||||
"users_username_key" UNIQUE CONSTRAINT, btree (username)
|
||||
```
|
||||
Great! Now let's check if running reverse migration also works:
|
||||
```
|
||||
migrate -database ${POSTGRESQL_URL} -path db/migrations down
|
||||
```
|
||||
Make sure to check if your database changed as expected in this case as well.
|
||||
|
||||
## Database transactions
|
||||
|
||||
To show database transactions usage, let's create another set of migrations by running:
|
||||
```
|
||||
migrate create -ext sql -dir db/migrations -seq add_mood_to_users
|
||||
```
|
||||
Again, it should create for us two migrations files:
|
||||
- 000002_add_mood_to_users.down.sql
|
||||
- 000002_add_mood_to_users.up.sql
|
||||
|
||||
In Postgres, when we want our queries to be done in a transaction, we need to wrap it with `BEGIN` and `COMMIT` commands.
|
||||
In our example, we are going to add a column to our database that can only accept enumerable values or NULL.
|
||||
Migration up:
|
||||
```
|
||||
BEGIN;
|
||||
|
||||
CREATE TYPE enum_mood AS ENUM (
|
||||
'happy',
|
||||
'sad',
|
||||
'neutral'
|
||||
);
|
||||
ALTER TABLE users ADD COLUMN mood enum_mood;
|
||||
|
||||
COMMIT;
|
||||
```
|
||||
Migration down:
|
||||
```
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE users DROP COLUMN mood;
|
||||
DROP TYPE enum_mood;
|
||||
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
Now we can run our new migration and check the database:
|
||||
```
|
||||
migrate -database ${POSTGRESQL_URL} -path db/migrations up
|
||||
psql example -c "\d users"
|
||||
```
|
||||
Expected output:
|
||||
```
|
||||
Table "public.users"
|
||||
Column | Type | Modifiers
|
||||
----------+------------------------+---------------------------------------------------------
|
||||
user_id | integer | not null default nextval('users_user_id_seq'::regclass)
|
||||
username | character varying(50) | not null
|
||||
password | character varying(50) | not null
|
||||
email | character varying(300) | not null
|
||||
mood | enum_mood |
|
||||
Indexes:
|
||||
"users_pkey" PRIMARY KEY, btree (user_id)
|
||||
"users_email_key" UNIQUE CONSTRAINT, btree (email)
|
||||
"users_username_key" UNIQUE CONSTRAINT, btree (username)
|
||||
```
|
||||
|
||||
## Optional: Run migrations within your Go app
|
||||
Here is a very simple app running migrations for the above configuration:
|
||||
```
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m, err := migrate.New(
|
||||
"file://db/migrations",
|
||||
"postgres://postgres:postgres@localhost:5432/example?sslmode=disable")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := m.Up(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
You can find details [here](README.md#use-in-your-go-project)
|
||||
|
||||
## Fix issue where migrations run twice
|
||||
|
||||
When the schema and role names are the same, you might run into issues if you create this schema using migrations.
|
||||
This is caused by the fact that the [default `search_path`](https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH) is `"$user", public`.
|
||||
In the first run (with an empty database) the migrate table is created in `public`.
|
||||
When the migrations create the `$user` schema, the next run will store (a new) migrate table in this schema (due to order of schemas in `search_path`) and tries to apply all migrations again (most likely failing).
|
||||
|
||||
To solve this you need to change the default `search_path` by removing the `$user` component, so the migrate table is always stored in the (available) `public` schema.
|
||||
This can be done using the [`search_path` query parameter in the URL](https://github.com/jexia/migrate/blob/fix-postgres-version-table/database/postgres/README.md#postgres).
|
||||
|
||||
For example to force the migrations table in the public schema you can use:
|
||||
```
|
||||
export POSTGRESQL_URL='postgres://postgres:password@localhost:5432/example?sslmode=disable&search_path=public'
|
||||
```
|
||||
|
||||
Note that you need to explicitly add the schema names to the table names in your migrations when you to modify the tables of the non-public schema.
|
||||
|
||||
Alternatively you can add the non-public schema manually (before applying the migrations) if that is possible in your case and let the tool store the migrations table in this schema as well.
|
||||
476
vendor/github.com/golang-migrate/migrate/v4/database/postgres/postgres.go
сгенерированный
поставляемый
476
vendor/github.com/golang-migrate/migrate/v4/database/postgres/postgres.go
сгенерированный
поставляемый
@@ -1,476 +0,0 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"go.uber.org/atomic"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
nurl "net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database"
|
||||
"github.com/golang-migrate/migrate/v4/database/multistmt"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db := Postgres{}
|
||||
database.Register("postgres", &db)
|
||||
database.Register("postgresql", &db)
|
||||
}
|
||||
|
||||
var (
|
||||
multiStmtDelimiter = []byte(";")
|
||||
|
||||
DefaultMigrationsTable = "schema_migrations"
|
||||
DefaultMultiStatementMaxSize = 10 * 1 << 20 // 10 MB
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNilConfig = fmt.Errorf("no config")
|
||||
ErrNoDatabaseName = fmt.Errorf("no database name")
|
||||
ErrNoSchema = fmt.Errorf("no schema")
|
||||
ErrDatabaseDirty = fmt.Errorf("database is dirty")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MigrationsTable string
|
||||
MigrationsTableQuoted bool
|
||||
MultiStatementEnabled bool
|
||||
DatabaseName string
|
||||
SchemaName string
|
||||
migrationsSchemaName string
|
||||
migrationsTableName string
|
||||
StatementTimeout time.Duration
|
||||
MultiStatementMaxSize int
|
||||
}
|
||||
|
||||
type Postgres struct {
|
||||
// Locking and unlocking need to use the same connection
|
||||
conn *sql.Conn
|
||||
db *sql.DB
|
||||
isLocked atomic.Bool
|
||||
|
||||
// Open and WithInstance need to guarantee that config is never nil
|
||||
config *Config
|
||||
}
|
||||
|
||||
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
|
||||
if config == nil {
|
||||
return nil, ErrNilConfig
|
||||
}
|
||||
|
||||
if err := instance.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.DatabaseName == "" {
|
||||
query := `SELECT CURRENT_DATABASE()`
|
||||
var databaseName string
|
||||
if err := instance.QueryRow(query).Scan(&databaseName); err != nil {
|
||||
return nil, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if len(databaseName) == 0 {
|
||||
return nil, ErrNoDatabaseName
|
||||
}
|
||||
|
||||
config.DatabaseName = databaseName
|
||||
}
|
||||
|
||||
if config.SchemaName == "" {
|
||||
query := `SELECT CURRENT_SCHEMA()`
|
||||
var schemaName string
|
||||
if err := instance.QueryRow(query).Scan(&schemaName); err != nil {
|
||||
return nil, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if len(schemaName) == 0 {
|
||||
return nil, ErrNoSchema
|
||||
}
|
||||
|
||||
config.SchemaName = schemaName
|
||||
}
|
||||
|
||||
if len(config.MigrationsTable) == 0 {
|
||||
config.MigrationsTable = DefaultMigrationsTable
|
||||
}
|
||||
|
||||
config.migrationsSchemaName = config.SchemaName
|
||||
config.migrationsTableName = config.MigrationsTable
|
||||
if config.MigrationsTableQuoted {
|
||||
re := regexp.MustCompile(`"(.*?)"`)
|
||||
result := re.FindAllStringSubmatch(config.MigrationsTable, -1)
|
||||
config.migrationsTableName = result[len(result)-1][1]
|
||||
if len(result) == 2 {
|
||||
config.migrationsSchemaName = result[0][1]
|
||||
} else if len(result) > 2 {
|
||||
return nil, fmt.Errorf("\"%s\" MigrationsTable contains too many dot characters", config.MigrationsTable)
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := instance.Conn(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
px := &Postgres{
|
||||
conn: conn,
|
||||
db: instance,
|
||||
config: config,
|
||||
}
|
||||
|
||||
if err := px.ensureVersionTable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return px, nil
|
||||
}
|
||||
|
||||
func (p *Postgres) Open(url string) (database.Driver, error) {
|
||||
purl, err := nurl.Parse(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("postgres", migrate.FilterCustomQuery(purl).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
migrationsTable := purl.Query().Get("x-migrations-table")
|
||||
migrationsTableQuoted := false
|
||||
if s := purl.Query().Get("x-migrations-table-quoted"); len(s) > 0 {
|
||||
migrationsTableQuoted, err = strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse option x-migrations-table-quoted: %w", err)
|
||||
}
|
||||
}
|
||||
if (len(migrationsTable) > 0) && (migrationsTableQuoted) && ((migrationsTable[0] != '"') || (migrationsTable[len(migrationsTable)-1] != '"')) {
|
||||
return nil, fmt.Errorf("x-migrations-table must be quoted (for instance '\"migrate\".\"schema_migrations\"') when x-migrations-table-quoted is enabled, current value is: %s", migrationsTable)
|
||||
}
|
||||
|
||||
statementTimeoutString := purl.Query().Get("x-statement-timeout")
|
||||
statementTimeout := 0
|
||||
if statementTimeoutString != "" {
|
||||
statementTimeout, err = strconv.Atoi(statementTimeoutString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
multiStatementMaxSize := DefaultMultiStatementMaxSize
|
||||
if s := purl.Query().Get("x-multi-statement-max-size"); len(s) > 0 {
|
||||
multiStatementMaxSize, err = strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if multiStatementMaxSize <= 0 {
|
||||
multiStatementMaxSize = DefaultMultiStatementMaxSize
|
||||
}
|
||||
}
|
||||
|
||||
multiStatementEnabled := false
|
||||
if s := purl.Query().Get("x-multi-statement"); len(s) > 0 {
|
||||
multiStatementEnabled, err = strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse option x-multi-statement: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
px, err := WithInstance(db, &Config{
|
||||
DatabaseName: purl.Path,
|
||||
MigrationsTable: migrationsTable,
|
||||
MigrationsTableQuoted: migrationsTableQuoted,
|
||||
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
|
||||
MultiStatementEnabled: multiStatementEnabled,
|
||||
MultiStatementMaxSize: multiStatementMaxSize,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return px, nil
|
||||
}
|
||||
|
||||
func (p *Postgres) Close() error {
|
||||
connErr := p.conn.Close()
|
||||
dbErr := p.db.Close()
|
||||
if connErr != nil || dbErr != nil {
|
||||
return fmt.Errorf("conn: %v, db: %v", connErr, dbErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://www.postgresql.org/docs/9.6/static/explicit-locking.html#ADVISORY-LOCKS
|
||||
func (p *Postgres) Lock() error {
|
||||
return database.CasRestoreOnErr(&p.isLocked, false, true, database.ErrLocked, func() error {
|
||||
aid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName, p.config.migrationsSchemaName, p.config.migrationsTableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This will wait indefinitely until the lock can be acquired.
|
||||
query := `SELECT pg_advisory_lock($1)`
|
||||
if _, err := p.conn.ExecContext(context.Background(), query, aid); err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Postgres) Unlock() error {
|
||||
return database.CasRestoreOnErr(&p.isLocked, true, false, database.ErrNotLocked, func() error {
|
||||
aid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName, p.config.migrationsSchemaName, p.config.migrationsTableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := `SELECT pg_advisory_unlock($1)`
|
||||
if _, err := p.conn.ExecContext(context.Background(), query, aid); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Postgres) Run(migration io.Reader) error {
|
||||
if p.config.MultiStatementEnabled {
|
||||
var err error
|
||||
if e := multistmt.Parse(migration, multiStmtDelimiter, p.config.MultiStatementMaxSize, func(m []byte) bool {
|
||||
if err = p.runStatement(m); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}); e != nil {
|
||||
return e
|
||||
}
|
||||
return err
|
||||
}
|
||||
migr, err := ioutil.ReadAll(migration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.runStatement(migr)
|
||||
}
|
||||
|
||||
func (p *Postgres) runStatement(statement []byte) error {
|
||||
ctx := context.Background()
|
||||
if p.config.StatementTimeout != 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, p.config.StatementTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
query := string(statement)
|
||||
if strings.TrimSpace(query) == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := p.conn.ExecContext(ctx, query); err != nil {
|
||||
if pgErr, ok := err.(*pq.Error); ok {
|
||||
var line uint
|
||||
var col uint
|
||||
var lineColOK bool
|
||||
if pgErr.Position != "" {
|
||||
if pos, err := strconv.ParseUint(pgErr.Position, 10, 64); err == nil {
|
||||
line, col, lineColOK = computeLineFromPos(query, int(pos))
|
||||
}
|
||||
}
|
||||
message := fmt.Sprintf("migration failed: %s", pgErr.Message)
|
||||
if lineColOK {
|
||||
message = fmt.Sprintf("%s (column %d)", message, col)
|
||||
}
|
||||
if pgErr.Detail != "" {
|
||||
message = fmt.Sprintf("%s, %s", message, pgErr.Detail)
|
||||
}
|
||||
return database.Error{OrigErr: err, Err: message, Query: statement, Line: line}
|
||||
}
|
||||
return database.Error{OrigErr: err, Err: "migration failed", Query: statement}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeLineFromPos(s string, pos int) (line uint, col uint, ok bool) {
|
||||
// replace crlf with lf
|
||||
s = strings.Replace(s, "\r\n", "\n", -1)
|
||||
// pg docs: pos uses index 1 for the first character, and positions are measured in characters not bytes
|
||||
runes := []rune(s)
|
||||
if pos > len(runes) {
|
||||
return 0, 0, false
|
||||
}
|
||||
sel := runes[:pos]
|
||||
line = uint(runesCount(sel, newLine) + 1)
|
||||
col = uint(pos - 1 - runesLastIndex(sel, newLine))
|
||||
return line, col, true
|
||||
}
|
||||
|
||||
const newLine = '\n'
|
||||
|
||||
func runesCount(input []rune, target rune) int {
|
||||
var count int
|
||||
for _, r := range input {
|
||||
if r == target {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func runesLastIndex(input []rune, target rune) int {
|
||||
for i := len(input) - 1; i >= 0; i-- {
|
||||
if input[i] == target {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (p *Postgres) SetVersion(version int, dirty bool) error {
|
||||
tx, err := p.conn.BeginTx(context.Background(), &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "transaction start failed"}
|
||||
}
|
||||
|
||||
query := `TRUNCATE ` + pq.QuoteIdentifier(p.config.migrationsSchemaName) + `.` + pq.QuoteIdentifier(p.config.migrationsTableName)
|
||||
if _, err := tx.Exec(query); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
// Also re-write the schema version for nil dirty versions to prevent
|
||||
// empty schema version for failed down migration on the first migration
|
||||
// See: https://github.com/golang-migrate/migrate/issues/330
|
||||
if version >= 0 || (version == database.NilVersion && dirty) {
|
||||
query = `INSERT INTO ` + pq.QuoteIdentifier(p.config.migrationsSchemaName) + `.` + pq.QuoteIdentifier(p.config.migrationsTableName) + ` (version, dirty) VALUES ($1, $2)`
|
||||
if _, err := tx.Exec(query, version, dirty); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Postgres) Version() (version int, dirty bool, err error) {
|
||||
query := `SELECT version, dirty FROM ` + pq.QuoteIdentifier(p.config.migrationsSchemaName) + `.` + pq.QuoteIdentifier(p.config.migrationsTableName) + ` LIMIT 1`
|
||||
err = p.conn.QueryRowContext(context.Background(), query).Scan(&version, &dirty)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return database.NilVersion, false, nil
|
||||
|
||||
case err != nil:
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Code.Name() == "undefined_table" {
|
||||
return database.NilVersion, false, nil
|
||||
}
|
||||
}
|
||||
return 0, false, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
|
||||
default:
|
||||
return version, dirty, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Postgres) Drop() (err error) {
|
||||
// select all tables in current schema
|
||||
query := `SELECT table_name FROM information_schema.tables WHERE table_schema=(SELECT current_schema()) AND table_type='BASE TABLE'`
|
||||
tables, err := p.conn.QueryContext(context.Background(), query)
|
||||
if err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
defer func() {
|
||||
if errClose := tables.Close(); errClose != nil {
|
||||
err = multierror.Append(err, errClose)
|
||||
}
|
||||
}()
|
||||
|
||||
// delete one table after another
|
||||
tableNames := make([]string, 0)
|
||||
for tables.Next() {
|
||||
var tableName string
|
||||
if err := tables.Scan(&tableName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tableName) > 0 {
|
||||
tableNames = append(tableNames, tableName)
|
||||
}
|
||||
}
|
||||
if err := tables.Err(); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if len(tableNames) > 0 {
|
||||
// delete one by one ...
|
||||
for _, t := range tableNames {
|
||||
query = `DROP TABLE IF EXISTS ` + pq.QuoteIdentifier(t) + ` CASCADE`
|
||||
if _, err := p.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureVersionTable checks if versions table exists and, if not, creates it.
|
||||
// Note that this function locks the database, which deviates from the usual
|
||||
// convention of "caller locks" in the Postgres type.
|
||||
func (p *Postgres) ensureVersionTable() (err error) {
|
||||
if err = p.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := p.Unlock(); e != nil {
|
||||
if err == nil {
|
||||
err = e
|
||||
} else {
|
||||
err = multierror.Append(err, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// This block checks whether the `MigrationsTable` already exists. This is useful because it allows read only postgres
|
||||
// users to also check the current version of the schema. Previously, even if `MigrationsTable` existed, the
|
||||
// `CREATE TABLE IF NOT EXISTS...` query would fail because the user does not have the CREATE permission.
|
||||
// Taken from https://github.com/mattes/migrate/blob/master/database/postgres/postgres.go#L258
|
||||
query := `SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2 LIMIT 1`
|
||||
row := p.conn.QueryRowContext(context.Background(), query, p.config.migrationsSchemaName, p.config.migrationsTableName)
|
||||
|
||||
var count int
|
||||
err = row.Scan(&count)
|
||||
if err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
if count == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
query = `CREATE TABLE IF NOT EXISTS ` + pq.QuoteIdentifier(p.config.migrationsSchemaName) + `.` + pq.QuoteIdentifier(p.config.migrationsTableName) + ` (version bigint not null primary key, dirty boolean not null)`
|
||||
if _, err = p.conn.ExecContext(context.Background(), query); err != nil {
|
||||
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
33
vendor/github.com/golang-migrate/migrate/v4/database/util.go
сгенерированный
поставляемый
33
vendor/github.com/golang-migrate/migrate/v4/database/util.go
сгенерированный
поставляемый
@@ -1,33 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.uber.org/atomic"
|
||||
"hash/crc32"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const advisoryLockIDSalt uint = 1486364155
|
||||
|
||||
// GenerateAdvisoryLockId inspired by rails migrations, see https://goo.gl/8o9bCT
|
||||
func GenerateAdvisoryLockId(databaseName string, additionalNames ...string) (string, error) { // nolint: golint
|
||||
if len(additionalNames) > 0 {
|
||||
databaseName = strings.Join(append(additionalNames, databaseName), "\x00")
|
||||
}
|
||||
sum := crc32.ChecksumIEEE([]byte(databaseName))
|
||||
sum = sum * uint32(advisoryLockIDSalt)
|
||||
return fmt.Sprint(sum), nil
|
||||
}
|
||||
|
||||
// CasRestoreOnErr CAS wrapper to automatically restore the lock state on error
|
||||
func CasRestoreOnErr(lock *atomic.Bool, o, n bool, casErr error, f func() error) error {
|
||||
if !lock.CAS(o, n) {
|
||||
return casErr
|
||||
}
|
||||
if err := f(); err != nil {
|
||||
// Automatically unlock/lock on error
|
||||
lock.Store(o)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
5
vendor/github.com/golang-migrate/migrate/v4/docker-deploy.sh
сгенерированный
поставляемый
5
vendor/github.com/golang-migrate/migrate/v4/docker-deploy.sh
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin && \
|
||||
docker build --build-arg VERSION="$TRAVIS_TAG" . -t migrate/migrate -t migrate/migrate:"$TRAVIS_TAG" && \
|
||||
docker push migrate/migrate:"$TRAVIS_TAG" && docker push migrate/migrate
|
||||
69
vendor/github.com/golang-migrate/migrate/v4/go.mod
сгенерированный
поставляемый
69
vendor/github.com/golang-migrate/migrate/v4/go.mod
сгенерированный
поставляемый
@@ -1,69 +0,0 @@
|
||||
module github.com/golang-migrate/migrate/v4
|
||||
|
||||
require (
|
||||
cloud.google.com/go/spanner v1.24.0
|
||||
cloud.google.com/go/storage v1.10.0
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.14
|
||||
github.com/ClickHouse/clickhouse-go v1.4.3
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30 // indirect
|
||||
github.com/aws/aws-sdk-go v1.17.7
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.0.2
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.1
|
||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.10.0
|
||||
github.com/dhui/dktest v0.3.7
|
||||
github.com/docker/docker v20.10.9+incompatible
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
|
||||
github.com/fsouza/fake-gcs-server v1.17.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gobuffalo/here v0.6.0
|
||||
github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/google/go-github/v35 v35.2.0
|
||||
github.com/gorilla/mux v1.7.4 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/jackc/pgconn v1.8.0
|
||||
github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451
|
||||
github.com/jackc/pgproto3/v2 v2.0.7 // indirect
|
||||
github.com/jackc/pgx/v4 v4.10.1
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/ktrysmt/go-bitbucket v0.6.4
|
||||
github.com/lib/pq v1.10.0
|
||||
github.com/markbates/pkger v0.15.1
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.0
|
||||
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8
|
||||
github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/snowflakedb/gosnowflake v1.6.3
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xanzy/go-gitlab v0.15.0
|
||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect
|
||||
go.mongodb.org/mongo-driver v1.7.0
|
||||
go.uber.org/atomic v1.6.0
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211013171255-e13a2654a71e // indirect
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect
|
||||
golang.org/x/tools v0.1.5
|
||||
google.golang.org/api v0.51.0
|
||||
google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
modernc.org/b v1.0.0 // indirect
|
||||
modernc.org/db v1.0.0 // indirect
|
||||
modernc.org/file v1.0.0 // indirect
|
||||
modernc.org/fileutil v1.0.0 // indirect
|
||||
modernc.org/golex v1.0.0 // indirect
|
||||
modernc.org/internal v1.0.0 // indirect
|
||||
modernc.org/lldb v1.0.0 // indirect
|
||||
modernc.org/ql v1.0.0
|
||||
modernc.org/sortutil v1.1.0 // indirect
|
||||
modernc.org/sqlite v1.10.6
|
||||
modernc.org/zappy v1.0.0 // indirect
|
||||
)
|
||||
|
||||
go 1.16
|
||||
1636
vendor/github.com/golang-migrate/migrate/v4/go.sum
сгенерированный
поставляемый
1636
vendor/github.com/golang-migrate/migrate/v4/go.sum
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
25
vendor/github.com/golang-migrate/migrate/v4/internal/url/url.go
сгенерированный
поставляемый
25
vendor/github.com/golang-migrate/migrate/v4/internal/url/url.go
сгенерированный
поставляемый
@@ -1,25 +0,0 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errNoScheme = errors.New("no scheme")
|
||||
var errEmptyURL = errors.New("URL cannot be empty")
|
||||
|
||||
// schemeFromURL returns the scheme from a URL string
|
||||
func SchemeFromURL(url string) (string, error) {
|
||||
if url == "" {
|
||||
return "", errEmptyURL
|
||||
}
|
||||
|
||||
i := strings.Index(url, ":")
|
||||
|
||||
// No : or : is the first character.
|
||||
if i < 1 {
|
||||
return "", errNoScheme
|
||||
}
|
||||
|
||||
return url[0:i], nil
|
||||
}
|
||||
12
vendor/github.com/golang-migrate/migrate/v4/log.go
сгенерированный
поставляемый
12
vendor/github.com/golang-migrate/migrate/v4/log.go
сгенерированный
поставляемый
@@ -1,12 +0,0 @@
|
||||
package migrate
|
||||
|
||||
// Logger is an interface so you can pass in your own
|
||||
// logging implementation.
|
||||
type Logger interface {
|
||||
|
||||
// Printf is like fmt.Printf
|
||||
Printf(format string, v ...interface{})
|
||||
|
||||
// Verbose should return true when verbose logging output is wanted
|
||||
Verbose() bool
|
||||
}
|
||||
981
vendor/github.com/golang-migrate/migrate/v4/migrate.go
сгенерированный
поставляемый
981
vendor/github.com/golang-migrate/migrate/v4/migrate.go
сгенерированный
поставляемый
@@ -1,981 +0,0 @@
|
||||
// Package migrate reads migrations from sources and runs them against databases.
|
||||
// Sources are defined by the `source.Driver` and databases by the `database.Driver`
|
||||
// interface. The driver interfaces are kept "dumb", all migration logic is kept
|
||||
// in this package.
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4/database"
|
||||
iurl "github.com/golang-migrate/migrate/v4/internal/url"
|
||||
"github.com/golang-migrate/migrate/v4/source"
|
||||
)
|
||||
|
||||
// DefaultPrefetchMigrations sets the number of migrations to pre-read
|
||||
// from the source. This is helpful if the source is remote, but has little
|
||||
// effect for a local source (i.e. file system).
|
||||
// Please note that this setting has a major impact on the memory usage,
|
||||
// since each pre-read migration is buffered in memory. See DefaultBufferSize.
|
||||
var DefaultPrefetchMigrations = uint(10)
|
||||
|
||||
// DefaultLockTimeout sets the max time a database driver has to acquire a lock.
|
||||
var DefaultLockTimeout = 15 * time.Second
|
||||
|
||||
var (
|
||||
ErrNoChange = errors.New("no change")
|
||||
ErrNilVersion = errors.New("no migration")
|
||||
ErrInvalidVersion = errors.New("version must be >= -1")
|
||||
ErrLocked = errors.New("database locked")
|
||||
ErrLockTimeout = errors.New("timeout: can't acquire database lock")
|
||||
)
|
||||
|
||||
// ErrShortLimit is an error returned when not enough migrations
|
||||
// can be returned by a source for a given limit.
|
||||
type ErrShortLimit struct {
|
||||
Short uint
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e ErrShortLimit) Error() string {
|
||||
return fmt.Sprintf("limit %v short", e.Short)
|
||||
}
|
||||
|
||||
type ErrDirty struct {
|
||||
Version int
|
||||
}
|
||||
|
||||
func (e ErrDirty) Error() string {
|
||||
return fmt.Sprintf("Dirty database version %v. Fix and force version.", e.Version)
|
||||
}
|
||||
|
||||
type Migrate struct {
|
||||
sourceName string
|
||||
sourceDrv source.Driver
|
||||
databaseName string
|
||||
databaseDrv database.Driver
|
||||
|
||||
// Log accepts a Logger interface
|
||||
Log Logger
|
||||
|
||||
// GracefulStop accepts `true` and will stop executing migrations
|
||||
// as soon as possible at a safe break point, so that the database
|
||||
// is not corrupted.
|
||||
GracefulStop chan bool
|
||||
isLockedMu *sync.Mutex
|
||||
|
||||
isGracefulStop bool
|
||||
isLocked bool
|
||||
|
||||
// PrefetchMigrations defaults to DefaultPrefetchMigrations,
|
||||
// but can be set per Migrate instance.
|
||||
PrefetchMigrations uint
|
||||
|
||||
// LockTimeout defaults to DefaultLockTimeout,
|
||||
// but can be set per Migrate instance.
|
||||
LockTimeout time.Duration
|
||||
}
|
||||
|
||||
// New returns a new Migrate instance from a source URL and a database URL.
|
||||
// The URL scheme is defined by each driver.
|
||||
func New(sourceURL, databaseURL string) (*Migrate, error) {
|
||||
m := newCommon()
|
||||
|
||||
sourceName, err := iurl.SchemeFromURL(sourceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.sourceName = sourceName
|
||||
|
||||
databaseName, err := iurl.SchemeFromURL(databaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.databaseName = databaseName
|
||||
|
||||
sourceDrv, err := source.Open(sourceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.sourceDrv = sourceDrv
|
||||
|
||||
databaseDrv, err := database.Open(databaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.databaseDrv = databaseDrv
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewWithDatabaseInstance returns a new Migrate instance from a source URL
|
||||
// and an existing database instance. The source URL scheme is defined by each driver.
|
||||
// Use any string that can serve as an identifier during logging as databaseName.
|
||||
// You are responsible for closing the underlying database client if necessary.
|
||||
func NewWithDatabaseInstance(sourceURL string, databaseName string, databaseInstance database.Driver) (*Migrate, error) {
|
||||
m := newCommon()
|
||||
|
||||
sourceName, err := iurl.SchemeFromURL(sourceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.sourceName = sourceName
|
||||
|
||||
m.databaseName = databaseName
|
||||
|
||||
sourceDrv, err := source.Open(sourceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.sourceDrv = sourceDrv
|
||||
|
||||
m.databaseDrv = databaseInstance
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewWithSourceInstance returns a new Migrate instance from an existing source instance
|
||||
// and a database URL. The database URL scheme is defined by each driver.
|
||||
// Use any string that can serve as an identifier during logging as sourceName.
|
||||
// You are responsible for closing the underlying source client if necessary.
|
||||
func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, databaseURL string) (*Migrate, error) {
|
||||
m := newCommon()
|
||||
|
||||
databaseName, err := iurl.SchemeFromURL(databaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.databaseName = databaseName
|
||||
|
||||
m.sourceName = sourceName
|
||||
|
||||
databaseDrv, err := database.Open(databaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.databaseDrv = databaseDrv
|
||||
|
||||
m.sourceDrv = sourceInstance
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewWithInstance returns a new Migrate instance from an existing source and
|
||||
// database instance. Use any string that can serve as an identifier during logging
|
||||
// as sourceName and databaseName. You are responsible for closing down
|
||||
// the underlying source and database client if necessary.
|
||||
func NewWithInstance(sourceName string, sourceInstance source.Driver, databaseName string, databaseInstance database.Driver) (*Migrate, error) {
|
||||
m := newCommon()
|
||||
|
||||
m.sourceName = sourceName
|
||||
m.databaseName = databaseName
|
||||
|
||||
m.sourceDrv = sourceInstance
|
||||
m.databaseDrv = databaseInstance
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func newCommon() *Migrate {
|
||||
return &Migrate{
|
||||
GracefulStop: make(chan bool, 1),
|
||||
PrefetchMigrations: DefaultPrefetchMigrations,
|
||||
LockTimeout: DefaultLockTimeout,
|
||||
isLockedMu: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the source and the database.
|
||||
func (m *Migrate) Close() (source error, database error) {
|
||||
databaseSrvClose := make(chan error)
|
||||
sourceSrvClose := make(chan error)
|
||||
|
||||
m.logVerbosePrintf("Closing source and database\n")
|
||||
|
||||
go func() {
|
||||
databaseSrvClose <- m.databaseDrv.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
sourceSrvClose <- m.sourceDrv.Close()
|
||||
}()
|
||||
|
||||
return <-sourceSrvClose, <-databaseSrvClose
|
||||
}
|
||||
|
||||
// Migrate looks at the currently active migration version,
|
||||
// then migrates either up or down to the specified version.
|
||||
func (m *Migrate) Migrate(version uint) error {
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curVersion, dirty, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
if dirty {
|
||||
return m.unlockErr(ErrDirty{curVersion})
|
||||
}
|
||||
|
||||
ret := make(chan interface{}, m.PrefetchMigrations)
|
||||
go m.read(curVersion, int(version), ret)
|
||||
|
||||
return m.unlockErr(m.runMigrations(ret))
|
||||
}
|
||||
|
||||
// Steps looks at the currently active migration version.
|
||||
// It will migrate up if n > 0, and down if n < 0.
|
||||
func (m *Migrate) Steps(n int) error {
|
||||
if n == 0 {
|
||||
return ErrNoChange
|
||||
}
|
||||
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curVersion, dirty, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
if dirty {
|
||||
return m.unlockErr(ErrDirty{curVersion})
|
||||
}
|
||||
|
||||
ret := make(chan interface{}, m.PrefetchMigrations)
|
||||
|
||||
if n > 0 {
|
||||
go m.readUp(curVersion, n, ret)
|
||||
} else {
|
||||
go m.readDown(curVersion, -n, ret)
|
||||
}
|
||||
|
||||
return m.unlockErr(m.runMigrations(ret))
|
||||
}
|
||||
|
||||
// Up looks at the currently active migration version
|
||||
// and will migrate all the way up (applying all up migrations).
|
||||
func (m *Migrate) Up() error {
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curVersion, dirty, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
if dirty {
|
||||
return m.unlockErr(ErrDirty{curVersion})
|
||||
}
|
||||
|
||||
ret := make(chan interface{}, m.PrefetchMigrations)
|
||||
|
||||
go m.readUp(curVersion, -1, ret)
|
||||
return m.unlockErr(m.runMigrations(ret))
|
||||
}
|
||||
|
||||
// Down looks at the currently active migration version
|
||||
// and will migrate all the way down (applying all down migrations).
|
||||
func (m *Migrate) Down() error {
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curVersion, dirty, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
if dirty {
|
||||
return m.unlockErr(ErrDirty{curVersion})
|
||||
}
|
||||
|
||||
ret := make(chan interface{}, m.PrefetchMigrations)
|
||||
go m.readDown(curVersion, -1, ret)
|
||||
return m.unlockErr(m.runMigrations(ret))
|
||||
}
|
||||
|
||||
// Drop deletes everything in the database.
|
||||
func (m *Migrate) Drop() error {
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.databaseDrv.Drop(); err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
return m.unlock()
|
||||
}
|
||||
|
||||
// Run runs any migration provided by you against the database.
|
||||
// It does not check any currently active version in database.
|
||||
// Usually you don't need this function at all. Use Migrate,
|
||||
// Steps, Up or Down instead.
|
||||
func (m *Migrate) Run(migration ...*Migration) error {
|
||||
if len(migration) == 0 {
|
||||
return ErrNoChange
|
||||
}
|
||||
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curVersion, dirty, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
if dirty {
|
||||
return m.unlockErr(ErrDirty{curVersion})
|
||||
}
|
||||
|
||||
ret := make(chan interface{}, m.PrefetchMigrations)
|
||||
|
||||
go func() {
|
||||
defer close(ret)
|
||||
for _, migr := range migration {
|
||||
if m.PrefetchMigrations > 0 && migr.Body != nil {
|
||||
m.logVerbosePrintf("Start buffering %v\n", migr.LogString())
|
||||
} else {
|
||||
m.logVerbosePrintf("Scheduled %v\n", migr.LogString())
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func(migr *Migration) {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}(migr)
|
||||
}
|
||||
}()
|
||||
|
||||
return m.unlockErr(m.runMigrations(ret))
|
||||
}
|
||||
|
||||
// Force sets a migration version.
|
||||
// It does not check any currently active version in database.
|
||||
// It resets the dirty state to false.
|
||||
func (m *Migrate) Force(version int) error {
|
||||
if version < -1 {
|
||||
return ErrInvalidVersion
|
||||
}
|
||||
|
||||
if err := m.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.databaseDrv.SetVersion(version, false); err != nil {
|
||||
return m.unlockErr(err)
|
||||
}
|
||||
|
||||
return m.unlock()
|
||||
}
|
||||
|
||||
// Version returns the currently active migration version.
|
||||
// If no migration has been applied, yet, it will return ErrNilVersion.
|
||||
func (m *Migrate) Version() (version uint, dirty bool, err error) {
|
||||
v, d, err := m.databaseDrv.Version()
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if v == database.NilVersion {
|
||||
return 0, false, ErrNilVersion
|
||||
}
|
||||
|
||||
return suint(v), d, nil
|
||||
}
|
||||
|
||||
// read reads either up or down migrations from source `from` to `to`.
|
||||
// Each migration is then written to the ret channel.
|
||||
// If an error occurs during reading, that error is written to the ret channel, too.
|
||||
// Once read is done reading it will close the ret channel.
|
||||
func (m *Migrate) read(from int, to int, ret chan<- interface{}) {
|
||||
defer close(ret)
|
||||
|
||||
// check if from version exists
|
||||
if from >= 0 {
|
||||
if err := m.versionExists(suint(from)); err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check if to version exists
|
||||
if to >= 0 {
|
||||
if err := m.versionExists(suint(to)); err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// no change?
|
||||
if from == to {
|
||||
ret <- ErrNoChange
|
||||
return
|
||||
}
|
||||
|
||||
if from < to {
|
||||
// it's going up
|
||||
// apply first migration if from is nil version
|
||||
if from == -1 {
|
||||
firstVersion, err := m.sourceDrv.First()
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(firstVersion, int(firstVersion))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
|
||||
from = int(firstVersion)
|
||||
}
|
||||
|
||||
// run until we reach target ...
|
||||
for from < to {
|
||||
if m.stop() {
|
||||
return
|
||||
}
|
||||
|
||||
next, err := m.sourceDrv.Next(suint(from))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(next, int(next))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
|
||||
from = int(next)
|
||||
}
|
||||
|
||||
} else {
|
||||
// it's going down
|
||||
// run until we reach target ...
|
||||
for from > to && from >= 0 {
|
||||
if m.stop() {
|
||||
return
|
||||
}
|
||||
|
||||
prev, err := m.sourceDrv.Prev(suint(from))
|
||||
if errors.Is(err, os.ErrNotExist) && to == -1 {
|
||||
// apply nil migration
|
||||
migr, err := m.newMigration(suint(from), -1)
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
|
||||
} else if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(suint(from), int(prev))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
|
||||
from = int(prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readUp reads up migrations from `from` limitted by `limit`.
|
||||
// limit can be -1, implying no limit and reading until there are no more migrations.
|
||||
// Each migration is then written to the ret channel.
|
||||
// If an error occurs during reading, that error is written to the ret channel, too.
|
||||
// Once readUp is done reading it will close the ret channel.
|
||||
func (m *Migrate) readUp(from int, limit int, ret chan<- interface{}) {
|
||||
defer close(ret)
|
||||
|
||||
// check if from version exists
|
||||
if from >= 0 {
|
||||
if err := m.versionExists(suint(from)); err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
ret <- ErrNoChange
|
||||
return
|
||||
}
|
||||
|
||||
count := 0
|
||||
for count < limit || limit == -1 {
|
||||
if m.stop() {
|
||||
return
|
||||
}
|
||||
|
||||
// apply first migration if from is nil version
|
||||
if from == -1 {
|
||||
firstVersion, err := m.sourceDrv.First()
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(firstVersion, int(firstVersion))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
from = int(firstVersion)
|
||||
count++
|
||||
continue
|
||||
}
|
||||
|
||||
// apply next migration
|
||||
next, err := m.sourceDrv.Next(suint(from))
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// no limit, but no migrations applied?
|
||||
if limit == -1 && count == 0 {
|
||||
ret <- ErrNoChange
|
||||
return
|
||||
}
|
||||
|
||||
// no limit, reached end
|
||||
if limit == -1 {
|
||||
return
|
||||
}
|
||||
|
||||
// reached end, and didn't apply any migrations
|
||||
if limit > 0 && count == 0 {
|
||||
ret <- os.ErrNotExist
|
||||
return
|
||||
}
|
||||
|
||||
// applied less migrations than limit?
|
||||
if count < limit {
|
||||
ret <- ErrShortLimit{suint(limit - count)}
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(next, int(next))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
from = int(next)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
// readDown reads down migrations from `from` limitted by `limit`.
|
||||
// limit can be -1, implying no limit and reading until there are no more migrations.
|
||||
// Each migration is then written to the ret channel.
|
||||
// If an error occurs during reading, that error is written to the ret channel, too.
|
||||
// Once readDown is done reading it will close the ret channel.
|
||||
func (m *Migrate) readDown(from int, limit int, ret chan<- interface{}) {
|
||||
defer close(ret)
|
||||
|
||||
// check if from version exists
|
||||
if from >= 0 {
|
||||
if err := m.versionExists(suint(from)); err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
ret <- ErrNoChange
|
||||
return
|
||||
}
|
||||
|
||||
// no change if already at nil version
|
||||
if from == -1 && limit == -1 {
|
||||
ret <- ErrNoChange
|
||||
return
|
||||
}
|
||||
|
||||
// can't go over limit if already at nil version
|
||||
if from == -1 && limit > 0 {
|
||||
ret <- os.ErrNotExist
|
||||
return
|
||||
}
|
||||
|
||||
count := 0
|
||||
for count < limit || limit == -1 {
|
||||
if m.stop() {
|
||||
return
|
||||
}
|
||||
|
||||
prev, err := m.sourceDrv.Prev(suint(from))
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// no limit or haven't reached limit, apply "first" migration
|
||||
if limit == -1 || limit-count > 0 {
|
||||
firstVersion, err := m.sourceDrv.First()
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(firstVersion, -1)
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
count++
|
||||
}
|
||||
|
||||
if count < limit {
|
||||
ret <- ErrShortLimit{suint(limit - count)}
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
migr, err := m.newMigration(suint(from), int(prev))
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
|
||||
ret <- migr
|
||||
go func() {
|
||||
if err := migr.Buffer(); err != nil {
|
||||
m.logErr(err)
|
||||
}
|
||||
}()
|
||||
from = int(prev)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
// runMigrations reads *Migration and error from a channel. Any other type
|
||||
// sent on this channel will result in a panic. Each migration is then
|
||||
// proxied to the database driver and run against the database.
|
||||
// Before running a newly received migration it will check if it's supposed
|
||||
// to stop execution because it might have received a stop signal on the
|
||||
// GracefulStop channel.
|
||||
func (m *Migrate) runMigrations(ret <-chan interface{}) error {
|
||||
for r := range ret {
|
||||
|
||||
if m.stop() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch r := r.(type) {
|
||||
case error:
|
||||
return r
|
||||
|
||||
case *Migration:
|
||||
migr := r
|
||||
|
||||
// set version with dirty state
|
||||
if err := m.databaseDrv.SetVersion(migr.TargetVersion, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if migr.Body != nil {
|
||||
m.logVerbosePrintf("Read and execute %v\n", migr.LogString())
|
||||
if err := m.databaseDrv.Run(migr.BufferedBody); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set clean state
|
||||
if err := m.databaseDrv.SetVersion(migr.TargetVersion, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endTime := time.Now()
|
||||
readTime := migr.FinishedReading.Sub(migr.StartedBuffering)
|
||||
runTime := endTime.Sub(migr.FinishedReading)
|
||||
|
||||
// log either verbose or normal
|
||||
if m.Log != nil {
|
||||
if m.Log.Verbose() {
|
||||
m.logPrintf("Finished %v (read %v, ran %v)\n", migr.LogString(), readTime, runTime)
|
||||
} else {
|
||||
m.logPrintf("%v (%v)\n", migr.LogString(), readTime+runTime)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown type: %T with value: %+v", r, r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// versionExists checks the source if either the up or down migration for
|
||||
// the specified migration version exists.
|
||||
func (m *Migrate) versionExists(version uint) (result error) {
|
||||
// try up migration first
|
||||
up, _, err := m.sourceDrv.ReadUp(version)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
if errClose := up.Close(); errClose != nil {
|
||||
result = multierror.Append(result, errClose)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
// then try down migration
|
||||
down, _, err := m.sourceDrv.ReadDown(version)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
if errClose := down.Close(); errClose != nil {
|
||||
result = multierror.Append(result, errClose)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fmt.Errorf("no migration found for version %d: %w", version, err)
|
||||
m.logErr(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// stop returns true if no more migrations should be run against the database
|
||||
// because a stop signal was received on the GracefulStop channel.
|
||||
// Calls are cheap and this function is not blocking.
|
||||
func (m *Migrate) stop() bool {
|
||||
if m.isGracefulStop {
|
||||
return true
|
||||
}
|
||||
|
||||
select {
|
||||
case <-m.GracefulStop:
|
||||
m.isGracefulStop = true
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// newMigration is a helper func that returns a *Migration for the
|
||||
// specified version and targetVersion.
|
||||
func (m *Migrate) newMigration(version uint, targetVersion int) (*Migration, error) {
|
||||
var migr *Migration
|
||||
|
||||
if targetVersion >= int(version) {
|
||||
r, identifier, err := m.sourceDrv.ReadUp(version)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// create "empty" migration
|
||||
migr, err = NewMigration(nil, "", version, targetVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
|
||||
} else {
|
||||
// create migration from up source
|
||||
migr, err = NewMigration(r, identifier, version, targetVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
r, identifier, err := m.sourceDrv.ReadDown(version)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// create "empty" migration
|
||||
migr, err = NewMigration(nil, "", version, targetVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
|
||||
} else {
|
||||
// create migration from down source
|
||||
migr, err = NewMigration(r, identifier, version, targetVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m.PrefetchMigrations > 0 && migr.Body != nil {
|
||||
m.logVerbosePrintf("Start buffering %v\n", migr.LogString())
|
||||
} else {
|
||||
m.logVerbosePrintf("Scheduled %v\n", migr.LogString())
|
||||
}
|
||||
|
||||
return migr, nil
|
||||
}
|
||||
|
||||
// lock is a thread safe helper function to lock the database.
|
||||
// It should be called as late as possible when running migrations.
|
||||
func (m *Migrate) lock() error {
|
||||
m.isLockedMu.Lock()
|
||||
defer m.isLockedMu.Unlock()
|
||||
|
||||
if m.isLocked {
|
||||
return ErrLocked
|
||||
}
|
||||
|
||||
// create done channel, used in the timeout goroutine
|
||||
done := make(chan bool, 1)
|
||||
defer func() {
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// use errchan to signal error back to this context
|
||||
errchan := make(chan error, 2)
|
||||
|
||||
// start timeout goroutine
|
||||
timeout := time.After(m.LockTimeout)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-timeout:
|
||||
errchan <- ErrLockTimeout
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// now try to acquire the lock
|
||||
go func() {
|
||||
if err := m.databaseDrv.Lock(); err != nil {
|
||||
errchan <- err
|
||||
} else {
|
||||
errchan <- nil
|
||||
}
|
||||
}()
|
||||
|
||||
// wait until we either receive ErrLockTimeout or error from Lock operation
|
||||
err := <-errchan
|
||||
if err == nil {
|
||||
m.isLocked = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// unlock is a thread safe helper function to unlock the database.
|
||||
// It should be called as early as possible when no more migrations are
|
||||
// expected to be executed.
|
||||
func (m *Migrate) unlock() error {
|
||||
m.isLockedMu.Lock()
|
||||
defer m.isLockedMu.Unlock()
|
||||
|
||||
if err := m.databaseDrv.Unlock(); err != nil {
|
||||
// BUG: Can potentially create a deadlock. Add a timeout.
|
||||
return err
|
||||
}
|
||||
|
||||
m.isLocked = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// unlockErr calls unlock and returns a combined error
|
||||
// if a prevErr is not nil.
|
||||
func (m *Migrate) unlockErr(prevErr error) error {
|
||||
if err := m.unlock(); err != nil {
|
||||
return multierror.Append(prevErr, err)
|
||||
}
|
||||
return prevErr
|
||||
}
|
||||
|
||||
// logPrintf writes to m.Log if not nil
|
||||
func (m *Migrate) logPrintf(format string, v ...interface{}) {
|
||||
if m.Log != nil {
|
||||
m.Log.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// logVerbosePrintf writes to m.Log if not nil. Use for verbose logging output.
|
||||
func (m *Migrate) logVerbosePrintf(format string, v ...interface{}) {
|
||||
if m.Log != nil && m.Log.Verbose() {
|
||||
m.Log.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// logErr writes error to m.Log if not nil
|
||||
func (m *Migrate) logErr(err error) {
|
||||
if m.Log != nil {
|
||||
m.Log.Printf("error: %v", err)
|
||||
}
|
||||
}
|
||||
160
vendor/github.com/golang-migrate/migrate/v4/migration.go
сгенерированный
поставляемый
160
vendor/github.com/golang-migrate/migrate/v4/migration.go
сгенерированный
поставляемый
@@ -1,160 +0,0 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultBufferSize sets the in memory buffer size (in Bytes) for every
|
||||
// pre-read migration (see DefaultPrefetchMigrations).
|
||||
var DefaultBufferSize = uint(100000)
|
||||
|
||||
// Migration holds information about a migration.
|
||||
// It is initially created from data coming from the source and then
|
||||
// used when run against the database.
|
||||
type Migration struct {
|
||||
// Identifier can be any string to help identifying
|
||||
// the migration in the source.
|
||||
Identifier string
|
||||
|
||||
// Version is the version of this migration.
|
||||
Version uint
|
||||
|
||||
// TargetVersion is the migration version after this migration
|
||||
// has been applied to the database.
|
||||
// Can be -1, implying that this is a NilVersion.
|
||||
TargetVersion int
|
||||
|
||||
// Body holds an io.ReadCloser to the source.
|
||||
Body io.ReadCloser
|
||||
|
||||
// BufferedBody holds an buffered io.Reader to the underlying Body.
|
||||
BufferedBody io.Reader
|
||||
|
||||
// BufferSize defaults to DefaultBufferSize
|
||||
BufferSize uint
|
||||
|
||||
// bufferWriter holds an io.WriteCloser and pipes to BufferBody.
|
||||
// It's an *Closer for flow control.
|
||||
bufferWriter io.WriteCloser
|
||||
|
||||
// Scheduled is the time when the migration was scheduled/ queued.
|
||||
Scheduled time.Time
|
||||
|
||||
// StartedBuffering is the time when buffering of the migration source started.
|
||||
StartedBuffering time.Time
|
||||
|
||||
// FinishedBuffering is the time when buffering of the migration source finished.
|
||||
FinishedBuffering time.Time
|
||||
|
||||
// FinishedReading is the time when the migration source is fully read.
|
||||
FinishedReading time.Time
|
||||
|
||||
// BytesRead holds the number of Bytes read from the migration source.
|
||||
BytesRead int64
|
||||
}
|
||||
|
||||
// NewMigration returns a new Migration and sets the body, identifier,
|
||||
// version and targetVersion. Body can be nil, which turns this migration
|
||||
// into a "NilMigration". If no identifier is provided, it will default to "<empty>".
|
||||
// targetVersion can be -1, implying it is a NilVersion.
|
||||
//
|
||||
// What is a NilMigration?
|
||||
// Usually each migration version coming from source is expected to have an
|
||||
// Up and Down migration. This is not a hard requirement though, leading to
|
||||
// a situation where only the Up or Down migration is present. So let's say
|
||||
// the user wants to migrate up to a version that doesn't have the actual Up
|
||||
// migration, in that case we still want to apply the version, but with an empty
|
||||
// body. We are calling that a NilMigration, a migration with an empty body.
|
||||
//
|
||||
// What is a NilVersion?
|
||||
// NilVersion is a const(-1). When running down migrations and we are at the
|
||||
// last down migration, there is no next down migration, the targetVersion should
|
||||
// be nil. Nil in this case is represented by -1 (because type int).
|
||||
func NewMigration(body io.ReadCloser, identifier string,
|
||||
version uint, targetVersion int) (*Migration, error) {
|
||||
tnow := time.Now()
|
||||
m := &Migration{
|
||||
Identifier: identifier,
|
||||
Version: version,
|
||||
TargetVersion: targetVersion,
|
||||
Scheduled: tnow,
|
||||
}
|
||||
|
||||
if body == nil {
|
||||
if len(identifier) == 0 {
|
||||
m.Identifier = "<empty>"
|
||||
}
|
||||
|
||||
m.StartedBuffering = tnow
|
||||
m.FinishedBuffering = tnow
|
||||
m.FinishedReading = tnow
|
||||
return m, nil
|
||||
}
|
||||
|
||||
br, bw := io.Pipe()
|
||||
m.Body = body // want to simulate low latency? newSlowReader(body)
|
||||
m.BufferSize = DefaultBufferSize
|
||||
m.BufferedBody = br
|
||||
m.bufferWriter = bw
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// String implements string.Stringer and is used in tests.
|
||||
func (m *Migration) String() string {
|
||||
return fmt.Sprintf("%v [%v=>%v]", m.Identifier, m.Version, m.TargetVersion)
|
||||
}
|
||||
|
||||
// LogString returns a string describing this migration to humans.
|
||||
func (m *Migration) LogString() string {
|
||||
directionStr := "u"
|
||||
if m.TargetVersion < int(m.Version) {
|
||||
directionStr = "d"
|
||||
}
|
||||
return fmt.Sprintf("%v/%v %v", m.Version, directionStr, m.Identifier)
|
||||
}
|
||||
|
||||
// Buffer buffers Body up to BufferSize.
|
||||
// Calling this function blocks. Call with goroutine.
|
||||
func (m *Migration) Buffer() error {
|
||||
if m.Body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.StartedBuffering = time.Now()
|
||||
|
||||
b := bufio.NewReaderSize(m.Body, int(m.BufferSize))
|
||||
|
||||
// start reading from body, peek won't move the read pointer though
|
||||
// poor man's solution?
|
||||
if _, err := b.Peek(int(m.BufferSize)); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
m.FinishedBuffering = time.Now()
|
||||
|
||||
// write to bufferWriter, this will block until
|
||||
// something starts reading from m.Buffer
|
||||
n, err := b.WriteTo(m.bufferWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.FinishedReading = time.Now()
|
||||
m.BytesRead = n
|
||||
|
||||
// close bufferWriter so Buffer knows that there is no
|
||||
// more data coming
|
||||
if err := m.bufferWriter.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// it's safe to close the Body too
|
||||
if err := m.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
43
vendor/github.com/golang-migrate/migrate/v4/source/go_bindata/README.md
сгенерированный
поставляемый
43
vendor/github.com/golang-migrate/migrate/v4/source/go_bindata/README.md
сгенерированный
поставляемый
@@ -1,43 +0,0 @@
|
||||
# go_bindata
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
|
||||
### Read bindata with NewWithSourceInstance
|
||||
|
||||
```shell
|
||||
go get -u github.com/jteeuwen/go-bindata/...
|
||||
cd examples/migrations && go-bindata -pkg migrations .
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/source/go_bindata"
|
||||
"github.com/golang-migrate/migrate/v4/source/go_bindata/examples/migrations"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// wrap assets into Resource
|
||||
s := bindata.Resource(migrations.AssetNames(),
|
||||
func(name string) ([]byte, error) {
|
||||
return migrations.Asset(name)
|
||||
})
|
||||
|
||||
d, err := bindata.WithInstance(s)
|
||||
m, err := migrate.NewWithSourceInstance("go-bindata", d, "database://foobar")
|
||||
m.Up() // run your migrations and handle the errors above of course
|
||||
}
|
||||
```
|
||||
|
||||
### Read bindata with URL (todo)
|
||||
|
||||
This will restore the assets in a tmp directory and then
|
||||
proxy to source/file. go-bindata must be in your `$PATH`.
|
||||
|
||||
```
|
||||
migrate -source go-bindata://examples/migrations/bindata.go
|
||||
```
|
||||
|
||||
|
||||
119
vendor/github.com/golang-migrate/migrate/v4/source/go_bindata/go-bindata.go
сгенерированный
поставляемый
119
vendor/github.com/golang-migrate/migrate/v4/source/go_bindata/go-bindata.go
сгенерированный
поставляемый
@@ -1,119 +0,0 @@
|
||||
package bindata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4/source"
|
||||
)
|
||||
|
||||
type AssetFunc func(name string) ([]byte, error)
|
||||
|
||||
func Resource(names []string, afn AssetFunc) *AssetSource {
|
||||
return &AssetSource{
|
||||
Names: names,
|
||||
AssetFunc: afn,
|
||||
}
|
||||
}
|
||||
|
||||
type AssetSource struct {
|
||||
Names []string
|
||||
AssetFunc AssetFunc
|
||||
}
|
||||
|
||||
func init() {
|
||||
source.Register("go-bindata", &Bindata{})
|
||||
}
|
||||
|
||||
type Bindata struct {
|
||||
path string
|
||||
assetSource *AssetSource
|
||||
migrations *source.Migrations
|
||||
}
|
||||
|
||||
func (b *Bindata) Open(url string) (source.Driver, error) {
|
||||
return nil, fmt.Errorf("not yet implemented")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNoAssetSource = fmt.Errorf("expects *AssetSource")
|
||||
)
|
||||
|
||||
func WithInstance(instance interface{}) (source.Driver, error) {
|
||||
if _, ok := instance.(*AssetSource); !ok {
|
||||
return nil, ErrNoAssetSource
|
||||
}
|
||||
as := instance.(*AssetSource)
|
||||
|
||||
bn := &Bindata{
|
||||
path: "<go-bindata>",
|
||||
assetSource: as,
|
||||
migrations: source.NewMigrations(),
|
||||
}
|
||||
|
||||
for _, fi := range as.Names {
|
||||
m, err := source.DefaultParse(fi)
|
||||
if err != nil {
|
||||
continue // ignore files that we can't parse
|
||||
}
|
||||
|
||||
if !bn.migrations.Append(m) {
|
||||
return nil, fmt.Errorf("unable to parse file %v", fi)
|
||||
}
|
||||
}
|
||||
|
||||
return bn, nil
|
||||
}
|
||||
|
||||
func (b *Bindata) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bindata) First() (version uint, err error) {
|
||||
if v, ok := b.migrations.First(); !ok {
|
||||
return 0, &os.PathError{Op: "first", Path: b.path, Err: os.ErrNotExist}
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bindata) Prev(version uint) (prevVersion uint, err error) {
|
||||
if v, ok := b.migrations.Prev(version); !ok {
|
||||
return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: b.path, Err: os.ErrNotExist}
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bindata) Next(version uint) (nextVersion uint, err error) {
|
||||
if v, ok := b.migrations.Next(version); !ok {
|
||||
return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: b.path, Err: os.ErrNotExist}
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bindata) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
|
||||
if m, ok := b.migrations.Up(version); ok {
|
||||
body, err := b.assetSource.AssetFunc(m.Raw)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return ioutil.NopCloser(bytes.NewReader(body)), m.Identifier, nil
|
||||
}
|
||||
return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: b.path, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
func (b *Bindata) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
|
||||
if m, ok := b.migrations.Down(version); ok {
|
||||
body, err := b.assetSource.AssetFunc(m.Raw)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return ioutil.NopCloser(bytes.NewReader(body)), m.Identifier, nil
|
||||
}
|
||||
return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: b.path, Err: os.ErrNotExist}
|
||||
}
|
||||
62
vendor/github.com/golang-migrate/migrate/v4/util.go
сгенерированный
поставляемый
62
vendor/github.com/golang-migrate/migrate/v4/util.go
сгенерированный
поставляемый
@@ -1,62 +0,0 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
nurl "net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MultiError holds multiple errors.
|
||||
//
|
||||
// Deprecated: Use github.com/hashicorp/go-multierror instead
|
||||
type MultiError struct {
|
||||
Errs []error
|
||||
}
|
||||
|
||||
// NewMultiError returns an error type holding multiple errors.
|
||||
//
|
||||
// Deprecated: Use github.com/hashicorp/go-multierror instead
|
||||
//
|
||||
func NewMultiError(errs ...error) MultiError {
|
||||
compactErrs := make([]error, 0)
|
||||
for _, e := range errs {
|
||||
if e != nil {
|
||||
compactErrs = append(compactErrs, e)
|
||||
}
|
||||
}
|
||||
return MultiError{compactErrs}
|
||||
}
|
||||
|
||||
// Error implements error. Multiple errors are concatenated with 'and's.
|
||||
func (m MultiError) Error() string {
|
||||
var strs = make([]string, 0)
|
||||
for _, e := range m.Errs {
|
||||
if len(e.Error()) > 0 {
|
||||
strs = append(strs, e.Error())
|
||||
}
|
||||
}
|
||||
return strings.Join(strs, " and ")
|
||||
}
|
||||
|
||||
// suint safely converts int to uint
|
||||
// see https://goo.gl/wEcqof
|
||||
// see https://goo.gl/pai7Dr
|
||||
func suint(n int) uint {
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("suint(%v) expects input >= 0", n))
|
||||
}
|
||||
return uint(n)
|
||||
}
|
||||
|
||||
// FilterCustomQuery filters all query values starting with `x-`
|
||||
func FilterCustomQuery(u *nurl.URL) *nurl.URL {
|
||||
ux := *u
|
||||
vx := make(nurl.Values)
|
||||
for k, v := range ux.Query() {
|
||||
if len(k) <= 1 || (len(k) > 1 && k[0:2] != "x-") {
|
||||
vx[k] = v
|
||||
}
|
||||
}
|
||||
ux.RawQuery = vx.Encode()
|
||||
return &ux
|
||||
}
|
||||
Ссылка в новой задаче
Block a user