mirror of https://github.com/status-im/migrate.git
Add WithInstance
This commit is contained in:
parent
da4160ce04
commit
1440a070c3
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
||||||
SOURCE ?= file go-bindata github aws-s3 google-cloud-storage
|
SOURCE ?= file go-bindata github aws-s3 google-cloud-storage
|
||||||
DATABASE ?= postgres mysql redshift cassandra sqlite3 spanner
|
DATABASE ?= postgres mysql redshift cassandra sqlite3 spanner clickhouse
|
||||||
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
|
VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-)
|
||||||
TEST_FLAGS ?=
|
TEST_FLAGS ?=
|
||||||
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
|
REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)")
|
||||||
|
|
|
@ -32,7 +32,7 @@ Database drivers run migrations. [Add a new database?](database/driver.go)
|
||||||
* [CrateDB](database/crate) ([todo #170](https://github.com/mattes/migrate/issues/170))
|
* [CrateDB](database/crate) ([todo #170](https://github.com/mattes/migrate/issues/170))
|
||||||
* [Shell](database/shell) ([todo #171](https://github.com/mattes/migrate/issues/171))
|
* [Shell](database/shell) ([todo #171](https://github.com/mattes/migrate/issues/171))
|
||||||
* [Google Cloud Spanner](database/spanner)
|
* [Google Cloud Spanner](database/spanner)
|
||||||
|
* [ClickHouse](database/clickhouse)
|
||||||
|
|
||||||
|
|
||||||
## Migration Sources
|
## Migration Sources
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
// +build clickhouse
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/kshvakov/clickhouse"
|
||||||
|
_ "github.com/mattes/migrate/database/clickhouse"
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ClickHouse
|
||||||
|
|
||||||
|
`clickhouse://host:port?username=user&password=qwerty&database=clicks`
|
||||||
|
|
||||||
|
| URL Query | Description |
|
||||||
|
|------------|-------------|
|
||||||
|
| `x-migrations-table`| Name of the migrations table |
|
||||||
|
| `database` | The name of the database to connect to |
|
||||||
|
| `username` | The user to sign in as |
|
||||||
|
| `password` | The user's password |
|
||||||
|
| `host` | The host to connect to. |
|
||||||
|
| `port` | The port to bind to. |
|
|
@ -0,0 +1,199 @@
|
||||||
|
package clickhouse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattes/migrate"
|
||||||
|
"github.com/mattes/migrate/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultMigrationsTable = "schema_migrations"
|
||||||
|
|
||||||
|
var ErrNilConfig = fmt.Errorf("no config")
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DatabaseName string
|
||||||
|
MigrationsTable string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
database.Register("clickhouse", &ClickHouse{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithInstance(conn *sql.DB, config *Config) (database.Driver, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, ErrNilConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := &ClickHouse{
|
||||||
|
conn: conn,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ch.init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ch.ensureVersionTable(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClickHouse struct {
|
||||||
|
conn *sql.DB
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) Open(dsn string) (database.Driver, error) {
|
||||||
|
purl, err := url.Parse(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := migrate.FilterCustomQuery(purl)
|
||||||
|
q.Scheme = "tcp"
|
||||||
|
conn, err := sql.Open("clickhouse", q.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = &ClickHouse{
|
||||||
|
conn: conn,
|
||||||
|
config: &Config{
|
||||||
|
MigrationsTable: purl.Query().Get("x-migrations-table"),
|
||||||
|
DatabaseName: purl.Query().Get("database"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ch.init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ch.ensureVersionTable(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) init() error {
|
||||||
|
if len(ch.config.DatabaseName) == 0 {
|
||||||
|
if err := ch.conn.QueryRow("SELECT currentDatabase()").Scan(ch.config.DatabaseName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ch.config.MigrationsTable) == 0 {
|
||||||
|
ch.config.MigrationsTable = DefaultMigrationsTable
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) Run(r io.Reader) error {
|
||||||
|
migration, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := ch.conn.Exec(string(migration)); err != nil {
|
||||||
|
return database.Error{OrigErr: err, Err: "migration failed", Query: migration}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (ch *ClickHouse) Version() (int, bool, error) {
|
||||||
|
var (
|
||||||
|
version int
|
||||||
|
dirty uint8
|
||||||
|
query = "SELECT version, dirty FROM `" + ch.config.MigrationsTable + "` ORDER BY sequence DESC LIMIT 1"
|
||||||
|
)
|
||||||
|
if err := ch.conn.QueryRow(query).Scan(&version, &dirty); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return database.NilVersion, false, nil
|
||||||
|
}
|
||||||
|
return 0, false, &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
return version, dirty == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) SetVersion(version int, dirty bool) error {
|
||||||
|
var (
|
||||||
|
bool = func(v bool) uint8 {
|
||||||
|
if v {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
tx, err = ch.conn.Begin()
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query := "INSERT INTO " + ch.config.MigrationsTable + " (version, dirty, sequence) VALUES (?, ?, ?)"
|
||||||
|
if _, err := tx.Exec(query, version, bool(dirty), time.Now().UnixNano()); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) ensureVersionTable() error {
|
||||||
|
var (
|
||||||
|
table string
|
||||||
|
query = "SHOW TABLES FROM " + ch.config.DatabaseName + " LIKE '" + ch.config.MigrationsTable + "'"
|
||||||
|
)
|
||||||
|
// check if migration table exists
|
||||||
|
if err := ch.conn.QueryRow(query).Scan(&table); 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 ` + ch.config.MigrationsTable + ` (
|
||||||
|
version UInt32,
|
||||||
|
dirty UInt8,
|
||||||
|
sequence UInt64
|
||||||
|
) Engine=TinyLog
|
||||||
|
`
|
||||||
|
if _, err := ch.conn.Exec(query); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) Drop() error {
|
||||||
|
var (
|
||||||
|
query = "SHOW TABLES FROM " + ch.config.DatabaseName
|
||||||
|
tables, err = ch.conn.Query(query)
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
defer tables.Close()
|
||||||
|
for tables.Next() {
|
||||||
|
var table string
|
||||||
|
if err := tables.Scan(&table); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
query = "DROP TABLE IF EXISTS " + ch.config.DatabaseName + "." + table
|
||||||
|
if _, err := ch.conn.Exec(query); err != nil {
|
||||||
|
return &database.Error{OrigErr: err, Query: []byte(query)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ch.ensureVersionTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *ClickHouse) Lock() error { return nil }
|
||||||
|
func (ch *ClickHouse) Unlock() error { return nil }
|
||||||
|
func (ch *ClickHouse) Close() error { return ch.conn.Close() }
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS test_1;
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE TABLE test_1 (
|
||||||
|
Date Date
|
||||||
|
) Engine=Memory;
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS test_2;
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE TABLE test_2 (
|
||||||
|
Date Date
|
||||||
|
) Engine=Memory;
|
Loading…
Reference in New Issue