Skip to content

Commit

Permalink
Export Trino errors with details
Browse files Browse the repository at this point in the history
  • Loading branch information
nineinchnick committed Apr 14, 2024
1 parent 7ac52e1 commit c2cfcf3
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 23 deletions.
56 changes: 51 additions & 5 deletions trino/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,56 @@ func TestIntegrationSelectFailedQuery(t *testing.T) {
rows.Close()
t.Fatal("query to invalid catalog succeeded")
}
_, ok := err.(*ErrQueryFailed)
queryFailed, ok := err.(*ErrQueryFailed)
if !ok {
t.Fatal("unexpected error:", err)
}
trinoErr, ok := errors.Unwrap(queryFailed).(*ErrTrino)
if !ok {
t.Fatal("unexpected error:", trinoErr)
}
expected := ErrTrino{
Message: "line 1:15: Catalog 'catalog' not found",
SqlState: "",
ErrorCode: 44,
ErrorName: "CATALOG_NOT_FOUND",
ErrorType: "USER_ERROR",
ErrorLocation: ErrorLocation{
LineNumber: 1,
ColumnNumber: 15,
},
FailureInfo: FailureInfo{
Type: "io.trino.spi.TrinoException",
Message: "line 1:15: Catalog 'catalog' not found",
},
}
if trinoErr.Message != expected.Message {
t.Fatalf("expected ErrTrino.Message to be `%s`, got: %s", expected.Message, trinoErr.Message)
}
if trinoErr.SqlState != expected.SqlState {
t.Fatalf("expected ErrTrino.SqlState to be `%s`, got: %s", expected.SqlState, trinoErr.SqlState)
}
if trinoErr.ErrorCode != expected.ErrorCode {
t.Fatalf("expected ErrTrino.ErrorCode to be `%d`, got: %d", expected.ErrorCode, trinoErr.ErrorCode)
}
if trinoErr.ErrorName != expected.ErrorName {
t.Fatalf("expected ErrTrino.ErrorName to be `%s`, got: %s", expected.ErrorName, trinoErr.ErrorName)
}
if trinoErr.ErrorType != expected.ErrorType {
t.Fatalf("expected ErrTrino.ErrorType to be `%s`, got: %s", expected.ErrorType, trinoErr.ErrorType)
}
if trinoErr.ErrorLocation.LineNumber != expected.ErrorLocation.LineNumber {
t.Fatalf("expected ErrTrino.ErrorLocation.LineNumber to be `%d`, got: %d", expected.ErrorLocation.LineNumber, trinoErr.ErrorLocation.LineNumber)
}
if trinoErr.ErrorLocation.ColumnNumber != expected.ErrorLocation.ColumnNumber {
t.Fatalf("expected ErrTrino.ErrorLocation.ColumnNumber to be `%d`, got: %d", expected.ErrorLocation.ColumnNumber, trinoErr.ErrorLocation.ColumnNumber)
}
if trinoErr.FailureInfo.Type != expected.FailureInfo.Type {
t.Fatalf("expected ErrTrino.FailureInfo.Type to be `%s`, got: %s", expected.FailureInfo.Type, trinoErr.FailureInfo.Type)
}
if trinoErr.FailureInfo.Message != expected.FailureInfo.Message {
t.Fatalf("expected ErrTrino.FailureInfo.Message to be `%s`, got: %s", expected.FailureInfo.Message, trinoErr.FailureInfo.Message)
}
}

type tpchRow struct {
Expand Down Expand Up @@ -486,13 +532,13 @@ func TestIntegrationQueryParametersSelect(t *testing.T) {
name: "invalid string as bigint",
query: "SELECT * FROM tpch.sf1.customer WHERE custkey=? LIMIT 2",
args: []interface{}{"1"},
expectedError: errors.New(`trino: query failed (200 OK): "io.trino.spi.TrinoException: line 1:46: Cannot apply operator: bigint = varchar(1)"`),
expectedError: errors.New(`trino: query failed (200 OK): "USER_ERROR: line 1:46: Cannot apply operator: bigint = varchar(1)"`),
},
{
name: "valid string as date",
query: "SELECT * FROM tpch.sf1.lineitem WHERE shipdate=? LIMIT 2",
args: []interface{}{"1995-01-27"},
expectedError: errors.New(`trino: query failed (200 OK): "io.trino.spi.TrinoException: line 1:47: Cannot apply operator: date = varchar(10)"`),
expectedError: errors.New(`trino: query failed (200 OK): "USER_ERROR: line 1:47: Cannot apply operator: date = varchar(10)"`),
},
}

Expand Down Expand Up @@ -611,11 +657,11 @@ func TestIntegrationUnsupportedHeader(t *testing.T) {
}{
{
query: "SET ROLE dummy",
err: errors.New(`trino: query failed (200 OK): "io.trino.spi.TrinoException: line 1:1: Role 'dummy' does not exist"`),
err: errors.New(`trino: query failed (200 OK): "USER_ERROR: line 1:1: Role 'dummy' does not exist"`),
},
{
query: "SET PATH dummy",
err: errors.New(`trino: query failed (200 OK): "io.trino.spi.TrinoException: SET PATH not supported by client"`),
err: errors.New(`trino: query failed (200 OK): "USER_ERROR: SET PATH not supported by client"`),
},
}
for _, c := range cases {
Expand Down
51 changes: 36 additions & 15 deletions trino/trino.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,11 @@ func (e *ErrQueryFailed) Error() string {
e.StatusCode, http.StatusText(e.StatusCode), e.Reason)
}

// Unwrap implements the unwrap interface.
func (e *ErrQueryFailed) Unwrap() error {
return e.Reason
}

func newErrQueryFailedFromResponse(resp *http.Response) *ErrQueryFailed {
const maxBytes = 8 * 1024
defer resp.Body.Close()
Expand Down Expand Up @@ -683,7 +688,7 @@ type stmtResponse struct {
InfoURI string `json:"infoUri"`
NextURI string `json:"nextUri"`
Stats stmtStats `json:"stats"`
Error stmtError `json:"error"`
Error ErrTrino `json:"error"`
UpdateType string `json:"updateType"`
UpdateCount int64 `json:"updateCount"`
}
Expand Down Expand Up @@ -712,27 +717,43 @@ type stmtStats struct {
RunningPercentage float32 `json:"runningPercentage"`
}

type stmtError struct {
Message string `json:"message"`
ErrorName string `json:"errorName"`
ErrorCode int `json:"errorCode"`
ErrorLocation stmtErrorLocation `json:"errorLocation"`
FailureInfo stmtErrorFailureInfo `json:"failureInfo"`
// Other fields omitted
type ErrTrino struct {
Message string `json:"message"`
SqlState string `json:"sqlState"`
ErrorCode int `json:"errorCode"`
ErrorName string `json:"errorName"`
ErrorType string `json:"errorType"`
ErrorLocation ErrorLocation `json:"errorLocation"`
FailureInfo FailureInfo `json:"failureInfo"`
}

type stmtErrorLocation struct {
type ErrorLocation struct {
LineNumber int `json:"lineNumber"`
ColumnNumber int `json:"columnNumber"`
}

type stmtErrorFailureInfo struct {
type FailureInfo struct {
Type string `json:"type"`
Message string `json:"message"`
Cause *FailureInfo `json:"cause"`
Suppressed []FailureInfo `json:"suppressed"`
Stack []string `json:"stack"`
ErrorInfo ErrorInfo `json:"errorInfo"`
ErrorLocation ErrorLocation `json:"errorLocation"`
}

func (i ErrTrino) Error() string {
return i.ErrorType + ": " + i.Message
}

type ErrorInfo struct {
Code int `json:"code"`
Name string `json:"name"`
Type string `json:"type"`
// Other fields omitted
}

func (e stmtError) Error() string {
return e.FailureInfo.Type + ": " + e.Message
func (i ErrorInfo) Error() string {
return fmt.Sprintf("%s: %s (%d)", i.Type, i.Name, i.Code)
}

type stmtStage struct {
Expand Down Expand Up @@ -1101,7 +1122,7 @@ type queryResponse struct {
Columns []queryColumn `json:"columns"`
Data []queryData `json:"data"`
Stats stmtStats `json:"stats"`
Error stmtError `json:"error"`
Error ErrTrino `json:"error"`
UpdateType string `json:"updateType"`
UpdateCount int64 `json:"updateCount"`
}
Expand Down Expand Up @@ -1149,7 +1170,7 @@ type typeArgument struct {
long int64
}

func handleResponseError(status int, respErr stmtError) error {
func handleResponseError(status int, respErr ErrTrino) error {
switch respErr.ErrorName {
case "":
return nil
Expand Down
6 changes: 3 additions & 3 deletions trino/trino_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func TestRoundTripRetryQueryError(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&stmtResponse{
Error: stmtError{
Error: ErrTrino{
ErrorName: "TEST",
},
})
Expand Down Expand Up @@ -920,7 +920,7 @@ func TestQueryCancellation(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&stmtResponse{
Error: stmtError{
Error: ErrTrino{
ErrorName: "USER_CANCELLED",
},
})
Expand Down Expand Up @@ -984,7 +984,7 @@ func TestFetchNoStackOverflow(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&stmtResponse{
Error: stmtError{
Error: ErrTrino{
ErrorName: "TEST",
},
})
Expand Down

0 comments on commit c2cfcf3

Please sign in to comment.