Source File
postgres.go
Belonging Package
github.com/golang-migrate/migrate/v4/database/postgres
package postgres
import (
nurl
multierror
)
func () {
:= Postgres{}
database.Register("postgres", &)
database.Register("postgresql", &)
}
var DefaultMigrationsTable = "schema_migrations"
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
DatabaseName string
SchemaName string
}
config *Config
}
func ( *sql.DB, *Config) (database.Driver, error) {
if == nil {
return nil, ErrNilConfig
}
if := .Ping(); != nil {
return nil,
}
:= `SELECT CURRENT_DATABASE()`
var string
if := .QueryRow().Scan(&); != nil {
return nil, &database.Error{OrigErr: , Query: []byte()}
}
if len() == 0 {
return nil, ErrNoDatabaseName
}
.DatabaseName =
= `SELECT CURRENT_SCHEMA()`
var string
if := .QueryRow().Scan(&); != nil {
return nil, &database.Error{OrigErr: , Query: []byte()}
}
if len() == 0 {
return nil, ErrNoSchema
}
.SchemaName =
if len(.MigrationsTable) == 0 {
.MigrationsTable = DefaultMigrationsTable
}
, := .Conn(context.Background())
if != nil {
return nil,
}
:= &Postgres{
conn: ,
db: ,
config: ,
}
if := .ensureVersionTable(); != nil {
return nil,
}
return , nil
}
func ( *Postgres) ( string) (database.Driver, error) {
, := nurl.Parse()
if != nil {
return nil,
}
, := sql.Open("postgres", migrate.FilterCustomQuery().String())
if != nil {
return nil,
}
:= .Query().Get("x-migrations-table")
, := WithInstance(, &Config{
DatabaseName: .Path,
MigrationsTable: ,
})
if != nil {
return nil,
}
return , nil
}
func ( *Postgres) () error {
:= .conn.Close()
:= .db.Close()
if != nil || != nil {
return fmt.Errorf("conn: %v, db: %v", , )
}
return nil
}
func ( *Postgres) () error {
if .isLocked {
return database.ErrLocked
}
, := database.GenerateAdvisoryLockId(.config.DatabaseName, .config.SchemaName)
if != nil {
return
}
:= `SELECT pg_advisory_lock($1)`
if , := .conn.ExecContext(context.Background(), , ); != nil {
return &database.Error{OrigErr: , Err: "try lock failed", Query: []byte()}
}
.isLocked = true
return nil
}
func ( *Postgres) () error {
if !.isLocked {
return nil
}
, := database.GenerateAdvisoryLockId(.config.DatabaseName, .config.SchemaName)
if != nil {
return
}
:= `SELECT pg_advisory_unlock($1)`
if , := .conn.ExecContext(context.Background(), , ); != nil {
return &database.Error{OrigErr: , Query: []byte()}
}
.isLocked = false
return nil
}
func ( *Postgres) ( io.Reader) error {
, := ioutil.ReadAll()
if != nil {
return
}
:= string([:])
if , := .conn.ExecContext(context.Background(), ); != nil {
if , := .(*pq.Error); {
var uint
var uint
var bool
if .Position != "" {
if , := strconv.ParseUint(.Position, 10, 64); == nil {
, , = computeLineFromPos(, int())
}
}
:= fmt.Sprintf("migration failed: %s", .Message)
if {
= fmt.Sprintf("%s (column %d)", , )
}
if .Detail != "" {
= fmt.Sprintf("%s, %s", , .Detail)
}
return database.Error{OrigErr: , Err: , Query: , Line: }
}
return database.Error{OrigErr: , Err: "migration failed", Query: }
}
return nil
}
:= []rune()
if > len() {
return 0, 0, false
}
:= [:]
= uint(runesCount(, newLine) + 1)
= uint( - 1 - runesLastIndex(, newLine))
return , , true
}
const newLine = '\n'
func ( []rune, rune) int {
var int
for , := range {
if == {
++
}
}
return
}
func ( []rune, rune) int {
for := len() - 1; >= 0; -- {
if [] == {
return
}
}
return -1
}
func ( *Postgres) ( int, bool) error {
, := .conn.BeginTx(context.Background(), &sql.TxOptions{})
if != nil {
return &database.Error{OrigErr: , Err: "transaction start failed"}
}
:= `TRUNCATE ` + pq.QuoteIdentifier(.config.MigrationsTable)
if , := .Exec(); != nil {
if := .Rollback(); != nil {
= multierror.Append(, )
}
return &database.Error{OrigErr: , Query: []byte()}
}
if >= 0 {
= `INSERT INTO ` + pq.QuoteIdentifier(.config.MigrationsTable) + ` (version, dirty) VALUES ($1, $2)`
if , := .Exec(, , ); != nil {
if := .Rollback(); != nil {
= multierror.Append(, )
}
return &database.Error{OrigErr: , Query: []byte()}
}
}
if := .Commit(); != nil {
return &database.Error{OrigErr: , Err: "transaction commit failed"}
}
return nil
}
func ( *Postgres) () ( int, bool, error) {
:= `SELECT version, dirty FROM ` + pq.QuoteIdentifier(.config.MigrationsTable) + ` LIMIT 1`
= .conn.QueryRowContext(context.Background(), ).Scan(&, &)
switch {
case == sql.ErrNoRows:
return database.NilVersion, false, nil
case != nil:
if , := .(*pq.Error); {
if .Code.Name() == "undefined_table" {
return database.NilVersion, false, nil
}
}
return 0, false, &database.Error{OrigErr: , Query: []byte()}
default:
return , , nil
}
}
:= `SELECT table_name FROM information_schema.tables WHERE table_schema=(SELECT current_schema()) AND table_type='BASE TABLE'`
, := .conn.QueryContext(context.Background(), )
if != nil {
return &database.Error{OrigErr: , Query: []byte()}
}
defer func() {
if := .Close(); != nil {
= multierror.Append(, )
}
}()
for , := range {
= `DROP TABLE IF EXISTS ` + pq.QuoteIdentifier() + ` CASCADE`
if , := .conn.ExecContext(context.Background(), ); != nil {
return &database.Error{OrigErr: , Query: []byte()}
}
}
}
return nil
}
func ( *Postgres) () ( error) {
if = .Lock(); != nil {
return
}
defer func() {
if := .Unlock(); != nil {
if == nil {
=
} else {
= multierror.Append(, )
}
}
}()
:= `CREATE TABLE IF NOT EXISTS ` + pq.QuoteIdentifier(.config.MigrationsTable) + ` (version bigint not null primary key, dirty boolean not null)`
if _, = .conn.ExecContext(context.Background(), ); != nil {
return &database.Error{OrigErr: , Query: []byte()}
}
return nil
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |