Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request vitessio#5690 from cmoog/pos_parse_err
Browse files Browse the repository at this point in the history
Adds context to sqlparser errors
  • Loading branch information
sougou authored Jan 19, 2020
2 parents 405ae09 + 5962d11 commit 824f835
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 8 deletions.
39 changes: 39 additions & 0 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,45 @@ func TestConvert(t *testing.T) {
}
}

func TestPositionedErr(t *testing.T) {
invalidSQL := []struct {
input string
output PositionedErr
}{{
input: "select convert('abc' as date) from t",
output: PositionedErr{"syntax error at position 24 near 'as'", 24, []byte("as")},
}, {
input: "select convert from t",
output: PositionedErr{"syntax error at position 20 near 'from'", 20, []byte("from")},
}, {
input: "select cast('foo', decimal) from t",
output: PositionedErr{"syntax error at position 19", 19, nil},
}, {
input: "select convert('abc', datetime(4+9)) from t",
output: PositionedErr{"syntax error at position 34", 34, nil},
}, {
input: "select convert('abc', decimal(4+9)) from t",
output: PositionedErr{"syntax error at position 33", 33, nil},
}, {
input: "set transaction isolation level 12345",
output: PositionedErr{"syntax error at position 38 near '12345'", 38, []byte("12345")},
}, {
input: "select * from a left join b",
output: PositionedErr{"syntax error at position 28", 28, nil},
}}

for _, tcase := range invalidSQL {
tkn := NewStringTokenizer(tcase.input)
_, err := ParseNext(tkn)

if posErr, ok := err.(PositionedErr); !ok {
t.Errorf("%s: %v expected PositionedErr, got (%T) %v", tcase.input, err, err, tcase.output)
} else if posErr.Pos != tcase.output.Pos || !bytes.Equal(posErr.Near, tcase.output.Near) || err.Error() != tcase.output.Err {
t.Errorf("%s: %v, want: %v", tcase.input, err, tcase.output)
}
}
}

func TestSubStr(t *testing.T) {

validSQL := []struct {
Expand Down
23 changes: 15 additions & 8 deletions go/vt/sqlparser/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package sqlparser

import (
"bytes"
"errors"
"fmt"
"io"

Expand Down Expand Up @@ -452,15 +451,23 @@ func (tkn *Tokenizer) Lex(lval *yySymType) int {
return typ
}

// PositionedErr holds context related to parser errors
type PositionedErr struct {
Err string
Pos int
Near []byte
}

func (p PositionedErr) Error() string {
if p.Near != nil {
return fmt.Sprintf("%s at position %v near '%s'", p.Err, p.Pos, p.Near)
}
return fmt.Sprintf("%s at position %v", p.Err, p.Pos)
}

// Error is called by go yacc if there's a parsing error.
func (tkn *Tokenizer) Error(err string) {
buf := &bytes2.Buffer{}
if tkn.lastToken != nil {
fmt.Fprintf(buf, "%s at position %v near '%s'", err, tkn.Position, tkn.lastToken)
} else {
fmt.Fprintf(buf, "%s at position %v", err, tkn.Position)
}
tkn.LastError = errors.New(buf.String())
tkn.LastError = PositionedErr{Err: err, Pos: tkn.Position, Near: tkn.lastToken}

// Try and re-sync to the next statement
tkn.skipStatement()
Expand Down

0 comments on commit 824f835

Please sign in to comment.