super-graph/serv/cmd_tern.go

232 lines
5.4 KiB
Go

package serv
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/dosco/super-graph/migrate"
"github.com/spf13/cobra"
)
var sampleMigration = `-- This is a sample migration.
create table users(
id serial primary key,
fullname varchar not null,
email varchar not null
);
---- create above / drop below ----
drop table users;
`
var newMigrationText = `-- Write your migrate up statements here
---- create above / drop below ----
-- Write your migrate down statements here. If this migration is irreversible
-- Then delete the separator line above.
`
func cmdNewMigration(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cmd.Help()
os.Exit(1)
}
name := args[0]
m, err := migrate.FindMigrations(conf.MigrationsPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading migrations:\n %v\n", err)
os.Exit(1)
}
mname := fmt.Sprintf("%03d_%s.sql", len(m)+100, name)
// Write new migration
mpath := filepath.Join(conf.MigrationsPath, mname)
mfile, err := os.OpenFile(mpath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0666)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer mfile.Close()
_, err = mfile.WriteString(newMigrationText)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
logger.Info().Msgf("created migration '%s'\n", mpath)
}
func cmdMigrate(cmd *cobra.Command, args []string) {
conn, err := initDB(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to database")
}
defer conn.Close(context.Background())
m, err := migrate.NewMigrator(conn, "schema_version")
if err != nil {
logger.Fatal().Err(err).Msg("failed to initializing migrator")
}
//m.Data = config.Data
err = m.LoadMigrations(conf.MigrationsPath)
if err != nil {
logger.Fatal().Err(err).Msg("failed to load migrations")
}
if len(m.Migrations) == 0 {
logger.Fatal().Msg("No migrations found")
}
m.OnStart = func(sequence int32, name, direction, sql string) {
logger.Info().Msgf("%s executing %s %s\n%s\n\n",
time.Now().Format("2006-01-02 15:04:05"), name, direction, sql)
}
var currentVersion int32
currentVersion, err = m.GetCurrentVersion()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get current version:\n %v\n", err)
os.Exit(1)
}
dest := args[0]
mustParseDestination := func(d string) int32 {
var n int64
n, err = strconv.ParseInt(d, 10, 32)
if err != nil {
logger.Fatal().Err(err).Msg("invalid destination")
}
return int32(n)
}
if dest == "last" {
err = m.Migrate()
} else if len(dest) >= 3 && dest[0:2] == "-+" {
err = m.MigrateTo(currentVersion - mustParseDestination(dest[2:]))
if err == nil {
err = m.MigrateTo(currentVersion)
}
} else if len(dest) >= 2 && dest[0] == '-' {
err = m.MigrateTo(currentVersion - mustParseDestination(dest[1:]))
} else if len(dest) >= 2 && dest[0] == '+' {
err = m.MigrateTo(currentVersion + mustParseDestination(dest[1:]))
} else {
//err = make(type, 0).MigrateTo(mustParseDestination(dest))
}
if err != nil {
logger.Info().Err(err).Send()
// logger.Info().Err(err).Send()
// if err, ok := err.(m.MigrationPgError); ok {
// if err.Detail != "" {
// logger.Info().Err(err).Msg(err.Detail)
// }
// if err.Position != 0 {
// ele, err := ExtractErrorLine(err.Sql, int(err.Position))
// if err != nil {
// logger.Fatal().Err(err).Send()
// }
// prefix := fmt.Sprintf()
// logger.Info().Msgf("line %d, %s%s", ele.LineNum, prefix, ele.Text)
// }
// }
// os.Exit(1)
}
logger.Info().Msg("migration done")
}
func cmdStatus(cmd *cobra.Command, args []string) {
conn, err := initDB(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to database")
}
defer conn.Close(context.Background())
m, err := migrate.NewMigrator(conn, "schema_version")
if err != nil {
logger.Fatal().Err(err).Msg("failed to initialize migrator")
}
//m.Data = config.Data
err = m.LoadMigrations(conf.MigrationsPath)
if err != nil {
logger.Fatal().Err(err).Msg("failed to load migrations")
}
if len(m.Migrations) == 0 {
logger.Fatal().Msg("no migrations found")
}
mver, err := m.GetCurrentVersion()
if err != nil {
logger.Fatal().Err(err).Msg("failed to retrieve migration")
}
var status string
behindCount := len(m.Migrations) - int(mver)
if behindCount == 0 {
status = "up to date"
} else {
status = "migration(s) pending"
}
fmt.Println("status: ", status)
fmt.Println("version: %d of %d\n", mver, len(m.Migrations))
fmt.Println("host: ", conf.DB.Host)
fmt.Println("database:", conf.DB.DBName)
}
type ErrorLineExtract struct {
LineNum int // Line number starting with 1
ColumnNum int // Column number starting with 1
Text string // Text of the line without a new line character.
}
// ExtractErrorLine takes source and character position extracts the line
// number, column number, and the line of text.
//
// The first character is position 1.
func ExtractErrorLine(source string, position int) (ErrorLineExtract, error) {
ele := ErrorLineExtract{LineNum: 1}
if position > len(source) {
return ele, fmt.Errorf("position (%d) is greater than source length (%d)", position, len(source))
}
lines := strings.SplitAfter(source, "\n")
for _, ele.Text = range lines {
if position-len(ele.Text) < 1 {
ele.ColumnNum = position
break
}
ele.LineNum += 1
position -= len(ele.Text)
}
ele.Text = strings.TrimSuffix(ele.Text, "\n")
return ele, nil
}