improve postgres errors and error formatting in general

This commit is contained in:
mattes 2014-08-13 01:32:14 +02:00
parent 2353a03eb4
commit fbfb50d4e7
3 changed files with 93 additions and 13 deletions

View File

@ -2,9 +2,12 @@ package postgres
import (
"database/sql"
_ "github.com/lib/pq"
"errors"
"fmt"
"github.com/lib/pq"
"github.com/mattes/migrate/file"
"github.com/mattes/migrate/migrate/direction"
"strconv"
)
type Driver struct {
@ -77,7 +80,16 @@ func (driver *Driver) Migrate(files file.Files, pipe chan interface{}) {
}
if _, err := tx.Exec(string(f.Content)); err != nil {
pipe <- err
pqErr := err.(*pq.Error)
offset, err := strconv.Atoi(pqErr.Position)
if err == nil && offset >= 0 {
lineNo, columnNo := file.LineColumnFromOffset(f.Content, offset-1)
errorPart := file.LinesBeforeAndAfter(f.Content, lineNo, 5, 5, true)
pipe <- errors.New(fmt.Sprintf("%s %v: %s in line %v, column %v:\n\n%s", pqErr.Severity, pqErr.Code, pqErr.Message, lineNo, columnNo, string(errorPart)))
} else {
pipe <- errors.New(fmt.Sprintf("%s %v: %s", pqErr.Severity, pqErr.Code, pqErr.Message))
}
if err := tx.Rollback(); err != nil {
pipe <- err
}

View File

@ -1,14 +1,17 @@
package file
import (
"bytes"
"errors"
"fmt"
"github.com/mattes/migrate/migrate/direction"
"go/token"
"io/ioutil"
"path"
"regexp"
"sort"
"strconv"
"strings"
)
var filenameRegex = "^([0-9]+)_(.*)\\.(up|down)\\.%s$"
@ -233,20 +236,69 @@ func parseFilenameSchema(filename string, filenameRegex *regexp.Regexp) (version
return version, matches[2], d, nil
}
// implement sort interface ...
// Len is the number of elements in the collection.
// Required by Sort Interface{}
func (mf MigrationFiles) Len() int {
return len(mf)
}
// Less reports whether the element with
// index i should sort before the element with index j.
// Required by Sort Interface{}
func (mf MigrationFiles) Less(i, j int) bool {
return mf[i].Version < mf[j].Version
}
// Swap swaps the elements with indexes i and j.
// Required by Sort Interface{}
func (mf MigrationFiles) Swap(i, j int) {
mf[i], mf[j] = mf[j], mf[i]
}
// LineColumnFromOffset reads data and returns line and column integer
// for a given offset.
// TODO is there a better way?
func LineColumnFromOffset(data []byte, offset int) (line, column int) {
fs := token.NewFileSet()
tf := fs.AddFile("", fs.Base(), len(data))
tf.SetLinesForContent(data)
pos := tf.Position(tf.Pos(offset))
return pos.Line, pos.Column
}
// LinesBeforeAndAfter reads n lines before and after a given line.
// Set lineNumbers to true, to prepend line numbers.
// TODO Trim empty lines at the beginning and at the end
// TODO Trim offset whitespace at the beginning of each line, so that indentation is preserved
func LinesBeforeAndAfter(data []byte, line, before, after int, lineNumbers bool) []byte {
startLine := line - before
endLine := line + after
lines := bytes.SplitN(data, []byte("\n"), endLine+1)
if startLine < 0 {
startLine = 0
}
if endLine > len(lines) {
endLine = len(lines)
}
selectLines := lines[startLine:endLine]
newLines := make([][]byte, 0)
lineCounter := startLine + 1
lineNumberDigits := len(strconv.Itoa(len(selectLines)))
for _, l := range selectLines {
lineCounterStr := strconv.Itoa(lineCounter)
if len(lineCounterStr)%lineNumberDigits != 0 {
lineCounterStr = strings.Repeat(" ", lineNumberDigits-len(lineCounterStr)%lineNumberDigits) + lineCounterStr
}
lNew := l
if lineNumbers {
lNew = append([]byte(lineCounterStr+": "), lNew...)
}
newLines = append(newLines, lNew)
lineCounter += 1
}
return bytes.Join(newLines, []byte("\n"))
}

34
main.go
View File

@ -3,12 +3,14 @@ package main
import (
"flag"
"fmt"
"github.com/fatih/color"
"github.com/mattes/migrate/file"
"github.com/mattes/migrate/migrate"
"github.com/mattes/migrate/migrate/direction"
pipep "github.com/mattes/migrate/pipe"
"os"
"strconv"
"strings"
"time"
)
@ -31,11 +33,10 @@ func main() {
case "migrate":
verifyMigrationsPath(*migrationsPath)
relativeN := flag.Arg(1)
relativeNInt, err := strconv.Atoi(relativeN)
if err != nil {
fmt.Println(err)
fmt.Println("Unable to parse parse param <n>.")
os.Exit(1)
}
migrateCmd(*url, *migrationsPath, relativeNInt)
@ -75,6 +76,15 @@ func verifyMigrationsPath(path string) {
}
}
func textPadding(text string) string {
textSplit := strings.Split(text, "\n")
newText := make([]string, 0)
for _, line := range textSplit {
newText = append(newText, " "+line)
}
return strings.Join(newText, "\n")
}
func writePipe(pipe chan interface{}) {
if pipe != nil {
for {
@ -84,21 +94,27 @@ func writePipe(pipe chan interface{}) {
return
} else {
switch item.(type) {
case string:
fmt.Println(" ", item.(string))
fmt.Println(item.(string))
case error:
fmt.Println(" ", item.(error).Error())
c := color.New(color.FgRed)
c.Println(item.(error).Error())
case file.File:
f := item.(file.File)
direc := " "
if f.Direction == direction.Up {
direc = " →"
fmt.Print("[ → ]")
} else if f.Direction == direction.Down {
direc = "← "
fmt.Print("[ ← ]")
}
fmt.Printf("%s %s\n", direc, f.FileName)
fmt.Printf(" %s\n", f.FileName)
default:
fmt.Printf(" %v\n", item)
text := fmt.Sprint(item)
fmt.Println(text)
}
}
}