Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ env:
- MYSQL_HOST=127.0.0.1
- MYSQL_PORT=3306
- MYSQL_DATABASE=kamimai
install:
- go get -d github.com/BurntSushi/toml
- go get -d github.com/go-sql-driver/mysql
- go get -d github.com/lib/pq
- go get -d github.com/stretchr/testify/assert
- go get -d gopkg.in/yaml.v2
- go get -u github.com/golang/lint/golint
before_install:
- mysql -e "CREATE DATABASE IF NOT EXISTS kamimai;" -uroot
- make init
Expand Down
1 change: 1 addition & 0 deletions driver/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ const versionTableName = "schema_version"

func init() {
core.RegisterDriver("mysql", &MySQL{})
core.RegisterDriver("postgres", &Postgres{})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any idea to resolve this..

}
217 changes: 217 additions & 0 deletions driver/postgres.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package driver

import (
"bytes"
"database/sql"
"errors"
"fmt"
"strings"
"sync"

"github.com/eure/kamimai/core"
"github.com/lib/pq"
)

type (
// Postgres driver object.
Postgres struct {
db *sql.DB
tx *sql.Tx
mu sync.Mutex
}
)

// Open is the first function to be called.
// Check the dsn string and open and verify any connection
// that has to be made.
func (d *Postgres) Open(dsn string) error {
z := strings.SplitN(dsn, "postgres:", 2)
if len(z) != 2 {
return errors.New("invalid data source name of postgres")
}

db, err := sql.Open("postgres", z[1])
if err != nil {
return err
}
if err := db.Ping(); err != nil {
return err
}
d.db = db

return d.Version().Create()
}

// Close is the last function to be called.
// Close any open connection here.
func (d *Postgres) Close() error {
return d.db.Close()
}

// Ext returns the sql file extension used by path. The extension is the
// suffix beginning at the final dot in the final element of path; it is
// empty if there is no dot.
func (d *Postgres) Ext() string {
return ".sql"
}

// Transaction starts a db transaction. The isolation level is dependent on the
// driver.
func (d *Postgres) Transaction(fn func(*sql.Tx) error) error {
d.mu.Lock()
defer func() {
d.tx = nil
d.mu.Unlock()
}()

tx, err := d.db.Begin()
if err != nil {
return err
}
d.tx = tx

// Procedure
if err := fn(d.tx); err != nil {
if rberr := d.tx.Rollback(); rberr != nil {
return rberr
}
return err
}

// Commit
if err := d.tx.Commit(); err != nil {
if rberr := d.tx.Rollback(); rberr != nil {
return rberr
}
return err
}

return nil
}

// Exec executes a query without returning any rows. The args are for any
// placeholder parameters in the query.
func (d *Postgres) Exec(query string, args ...interface{}) (sql.Result, error) {
if d.tx != nil {
return d.tx.Exec(query, args...)
}
return d.db.Exec(query, args...)
}

// Version returns a version interface.
func (d *Postgres) Version() core.Version {
return d
}

// Migrate applies migration file.
func (d *Postgres) Migrate(m *core.Migration) error {
b, err := m.Read()
if err != nil {
return err
}

stmts := bytes.Split(b, []byte(";"))
for _, stmt := range stmts {
query := strings.TrimSpace(string(stmt))
if len(query) == 0 {
continue
}
_, err = d.Exec(query)
if err != nil {
isWarn := strings.Contains(err.Error(), pq.Ewarning)
if !isWarn {
return err
}
}
}

return nil
}

// Insert inserts the given migration version.
func (d *Postgres) Insert(val uint64) error {
query := fmt.Sprintf(`INSERT INTO %s (version) VALUES (%d)`,
versionTableName, val)

_, err := d.Exec(query)
if err != nil {
isWarn := strings.Contains(err.Error(), pq.Ewarning)
if !isWarn {
return err
}
}
return nil
}

// Delete deletes the given migration version.
func (d *Postgres) Delete(val uint64) error {
query := fmt.Sprintf(`DELETE FROM %s WHERE version = %d`,
versionTableName, val)

_, err := d.Exec(query)
if err != nil {
isWarn := strings.Contains(err.Error(), pq.Ewarning)
if !isWarn {
return err
}
}
return nil
}

// Count counts number of row the given migration version.
func (d *Postgres) Count(val uint64) int {
query := fmt.Sprintf(`SELECT count(version) count FROM %s WHERE version = %d`,
versionTableName, val)

var count int
if err := d.db.QueryRow(query).Scan(&count); err != nil {
return 0
}
return count
}

// Current returns the current migration version.
func (d *Postgres) Current() (uint64, error) {
const query = `SELECT version FROM ` +
versionTableName + ` ORDER BY version DESC LIMIT 1`

var version uint64
err := d.db.QueryRow(query).Scan(&version)
switch {
case err == sql.ErrNoRows:
return 0, nil
case err != nil:
return 0, err
}
return version, nil
}

// Create creates
func (d *Postgres) Create() error {
const query = `CREATE TABLE IF NOT EXISTS ` +
versionTableName + ` (version BIGINT NOT NULL PRIMARY KEY);`

_, err := d.Exec(query)

if err != nil {
isWarn := strings.Contains(err.Error(), pq.Ewarning)
if !isWarn {
return err
}
}
return nil
}

// Drop drops
func (d *Postgres) Drop() error {
const query = `DROP TABLE IF EXISTS ` + versionTableName

_, err := d.Exec(query)
if err != nil {
isWarn := strings.Contains(err.Error(), pq.Ewarning)
if !isWarn {
return err
}
}
return nil
}
8 changes: 8 additions & 0 deletions examples/postgres/config.tml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[develop]
driver = "postgres"
dsn = "postgres:user=$POSTGRES_USER dbname=hogedb password=$POSTGRES_PASSWORD sslmode=disable"

[test1]
driver = "postgres"
dsn = "postgres:user=$POSTGRES_USER dbname=hogedb password=$POSTGRES_PASSWORD sslmode=disable"
directory = "test1"
1 change: 1 addition & 0 deletions examples/postgres/migrations/001_create_product_down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE product;
4 changes: 4 additions & 0 deletions examples/postgres/migrations/001_create_product_up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE TABLE product (
id int4 PRIMARY KEY,
name text
);
1 change: 1 addition & 0 deletions examples/postgres/test1/001_create_product_down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE product;
4 changes: 4 additions & 0 deletions examples/postgres/test1/001_create_product_up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE TABLE product (
id int4 PRIMARY KEY,
name text
);
1 change: 1 addition & 0 deletions examples/postgres/test1/002_insert_product_down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM product WHERE id in (1, 2);
1 change: 1 addition & 0 deletions examples/postgres/test1/002_insert_product_up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO product (id, name) VALUES (1, 'prod1'), (2, 'prod2');
1 change: 1 addition & 0 deletions examples/postgres/test1/003_insert_product_down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM product WHERE id in (3, 4);
1 change: 1 addition & 0 deletions examples/postgres/test1/003_insert_product_up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO product (id, name) VALUES (3, 'prod3'), (4, 'prod4');
22 changes: 7 additions & 15 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import:
- package: github.com/BurntSushi/toml
- package: github.com/go-sql-driver/mysql
- package: gopkg.in/yaml.v2
- package: github.com/lib/pq
testImport:
- package: github.com/stretchr/testify
subpackages:
Expand Down