Merge pull request #127 from vporoshok/master

Add support for multi-schema migrations in Postgres
This commit is contained in:
Dale Hui 2018-11-05 22:34:54 -08:00 committed by GitHub
commit 1df8057f97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 4 deletions

View File

@ -35,6 +35,7 @@ var (
type Config struct {
MigrationsTable string
DatabaseName string
SchemaName string
}
type Postgres struct {
@ -68,6 +69,18 @@ func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
config.DatabaseName = databaseName
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
}
@ -133,7 +146,7 @@ func (p *Postgres) Lock() error {
return database.ErrLocked
}
aid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName)
aid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName, p.config.SchemaName)
if err != nil {
return err
}

View File

@ -178,6 +178,56 @@ func TestWithSchema(t *testing.T) {
})
}
func TestParallelSchema(t *testing.T) {
mt.ParallelTest(t, versions, isReady,
func(t *testing.T, i mt.Instance) {
p := &Postgres{}
addr := pgConnectionString(i.Host(), i.Port())
d, err := p.Open(addr)
if err != nil {
t.Fatalf("%v", err)
}
defer d.Close()
// create foo and bar schemas
if err := d.Run(bytes.NewReader([]byte("CREATE SCHEMA foo AUTHORIZATION postgres"))); err != nil {
t.Fatal(err)
}
if err := d.Run(bytes.NewReader([]byte("CREATE SCHEMA bar AUTHORIZATION postgres"))); err != nil {
t.Fatal(err)
}
// re-connect using that schemas
dfoo, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=foo", i.Host(), i.Port()))
if err != nil {
t.Fatalf("%v", err)
}
defer dfoo.Close()
dbar, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=bar", i.Host(), i.Port()))
if err != nil {
t.Fatalf("%v", err)
}
defer dbar.Close()
if err := dfoo.Lock(); err != nil {
t.Fatal(err)
}
if err := dbar.Lock(); err != nil {
t.Fatal(err)
}
if err := dbar.Unlock(); err != nil {
t.Fatal(err)
}
if err := dfoo.Unlock(); err != nil {
t.Fatal(err)
}
})
}
func TestWithInstance(t *testing.T) {
}

View File

@ -3,12 +3,16 @@ package database
import (
"fmt"
"hash/crc32"
"strings"
)
const advisoryLockIdSalt uint = 1486364155
// GenerateAdvisoryLockId inspired by rails migrations, see https://goo.gl/8o9bCT
func GenerateAdvisoryLockId(databaseName string) (string, error) {
func GenerateAdvisoryLockId(databaseName string, additionalNames ...string) (string, error) {
if len(additionalNames) > 0 {
databaseName = strings.Join(append(additionalNames, databaseName), "\x00")
}
sum := crc32.ChecksumIEEE([]byte(databaseName))
sum = sum * uint32(advisoryLockIdSalt)
return fmt.Sprintf("%v", sum), nil

View File

@ -7,14 +7,33 @@ import (
func TestGenerateAdvisoryLockId(t *testing.T) {
testcases := []struct {
dbname string
additional []string
expectedID string // empty string signifies that an error is expected
}{
{dbname: "database_name", expectedID: "1764327054"},
{
dbname: "database_name",
expectedID: "1764327054",
},
{
dbname: "database_name",
additional: []string{"schema_name_1"},
expectedID: "2453313553",
},
{
dbname: "database_name",
additional: []string{"schema_name_2"},
expectedID: "235207038",
},
{
dbname: "database_name",
additional: []string{"schema_name_1", "schema_name_2"},
expectedID: "3743845847",
},
}
for _, tc := range testcases {
t.Run(tc.dbname, func(t *testing.T) {
if id, err := GenerateAdvisoryLockId("database_name"); err == nil {
if id, err := GenerateAdvisoryLockId(tc.dbname, tc.additional...); err == nil {
if id != tc.expectedID {
t.Error("Generated incorrect ID:", id, "!=", tc.expectedID)
}