Skip to content

Commit

Permalink
Merge pull request #7514 from planetscale/fix-vtgate-errors
Browse files Browse the repository at this point in the history
Change Error Messages In VTGate
  • Loading branch information
harshit-gangal authored Mar 2, 2021
2 parents 1bc7267 + 2ab202b commit 451279d
Show file tree
Hide file tree
Showing 98 changed files with 742 additions and 587 deletions.
4 changes: 2 additions & 2 deletions go/mysql/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ func (c *Conn) handleComPing() bool {
c.recycleReadPacket()
// Return error if listener was shut down and OK otherwise
if c.listener.isShutdown() {
if !c.writeErrorAndLog(ERServerShutdown, SSServerShutdown, "Server shutdown in progress") {
if !c.writeErrorAndLog(ERServerShutdown, SSNetError, "Server shutdown in progress") {
return false
}
} else {
Expand Down Expand Up @@ -1214,7 +1214,7 @@ func (c *Conn) handleComQuery(handler Handler, data []byte) (kontinue bool) {
}

if len(queries) == 0 {
err := NewSQLError(EREmptyQuery, SSSyntaxErrorOrAccessViolation, "Query was empty")
err := NewSQLError(EREmptyQuery, SSClientError, "Query was empty")
return c.writeErrorPacketFromErrorAndLog(err)
}

Expand Down
45 changes: 29 additions & 16 deletions go/mysql/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,12 @@ const (
// unknown
ERUnknownError = 1105

// internal
ERInternalError = 1815

// unimplemented
ERNotSupportedYet = 1235
ERUnsupportedPS = 1295

// resource exhausted
ERDiskFull = 1021
Expand Down Expand Up @@ -393,9 +397,11 @@ const (
ERFeatureDisabled = 1289
EROptionPreventsStatement = 1290
ERDuplicatedValueInType = 1291
ERSPDoesNotExist = 1305
ERRowIsReferenced2 = 1451
ErNoReferencedRow2 = 1452
ErSPNotVarArg = 1414
ERInnodbReadOnly = 1874

// already exists
ERTableExists = 1050
Expand Down Expand Up @@ -471,6 +477,7 @@ const (
ERBlobKeyWithoutLength = 1170
ERPrimaryCantHaveNull = 1171
ERTooManyRows = 1172
ERLockOrActiveTransaction = 1192
ERUnknownSystemVariable = 1193
ERSetConstantsOnly = 1204
ERWrongArguments = 1210
Expand Down Expand Up @@ -501,13 +508,13 @@ const (
ERInvalidOnUpdate = 1294
ERUnknownTimeZone = 1298
ERInvalidCharacterString = 1300
ERSavepointNotExist = 1305
ERIllegalReference = 1247
ERDerivedMustHaveAlias = 1248
ERTableNameNotAllowedHere = 1250
ERQueryInterrupted = 1317
ERTruncatedWrongValueForField = 1366
ERDataTooLong = 1406
ERForbidSchemaChange = 1450
ERDataOutOfRange = 1690
)

Expand All @@ -520,32 +527,23 @@ const (
// in client.c. So using that one.
SSUnknownSQLState = "HY000"

//SSSyntaxErrorOrAccessViolation is the state on syntax errors or access violations
SSSyntaxErrorOrAccessViolation = "42000"

// SSUnknownComError is ER_UNKNOWN_COM_ERROR
SSUnknownComError = "08S01"

// SSHandshakeError is ER_HANDSHAKE_ERROR
SSHandshakeError = "08S01"
// SSNetError is network related error
SSNetError = "08S01"

// SSServerShutdown is ER_SERVER_SHUTDOWN
SSServerShutdown = "08S01"
// SSWrongNumberOfColumns is related to columns error
SSWrongNumberOfColumns = "21000"

// SSDataTooLong is ER_DATA_TOO_LONG
SSDataTooLong = "22001"

// SSDataOutOfRange is ER_DATA_OUT_OF_RANGE
SSDataOutOfRange = "22003"

// SSBadNullError is ER_BAD_NULL_ERROR
SSBadNullError = "23000"

// SSBadFieldError is ER_BAD_FIELD_ERROR
SSBadFieldError = "42S22"

// SSDupKey is ER_DUP_KEY
SSDupKey = "23000"
// SSConstraintViolation is constraint violation
SSConstraintViolation = "23000"

// SSCantDoThisDuringAnTransaction is
// ER_CANT_DO_THIS_DURING_AN_TRANSACTION
Expand All @@ -554,8 +552,23 @@ const (
// SSAccessDeniedError is ER_ACCESS_DENIED_ERROR
SSAccessDeniedError = "28000"

// SSNoDB is ER_NO_DB_ERROR
SSNoDB = "3D000"

// SSLockDeadlock is ER_LOCK_DEADLOCK
SSLockDeadlock = "40001"

//SSClientError is the state on client errors
SSClientError = "42000"

// SSBadFieldError is ER_BAD_FIELD_ERROR
SSBadFieldError = "42S22"

// SSUnknownTable is ER_UNKNOWN_TABLE
SSUnknownTable = "42S02"

// SSQueryInterrupted is ER_QUERY_INTERRUPTED;
SSQueryInterrupted = "70100"
)

// A few interesting character set values.
Expand Down
2 changes: 1 addition & 1 deletion go/mysql/endtoend/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func TestDupEntry(t *testing.T) {
t.Fatalf("first insert failed: %v", err)
}
_, err = conn.ExecuteFetch("insert into dup_entry(id, name) values(2, 10)", 0, false)
assertSQLError(t, err, mysql.ERDupEntry, mysql.SSDupKey, "Duplicate entry", "insert into dup_entry(id, name) values(2, 10)")
assertSQLError(t, err, mysql.ERDupEntry, mysql.SSConstraintViolation, "Duplicate entry", "insert into dup_entry(id, name) values(2, 10)")
}

// TestClientFoundRows tests if the CLIENT_FOUND_ROWS flag works.
Expand Down
45 changes: 17 additions & 28 deletions go/mysql/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package mysql

import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
Expand All @@ -29,8 +30,6 @@ import (
"testing"
"time"

"context"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -1151,25 +1150,25 @@ func TestErrorCodes(t *testing.T) {
{
err: vterrors.Errorf(
vtrpcpb.Code_INVALID_ARGUMENT,
"(errno %v) (sqlstate %v) invalid argument with errno", ERDupEntry, SSDupKey),
"(errno %v) (sqlstate %v) invalid argument with errno", ERDupEntry, SSConstraintViolation),
code: ERDupEntry,
sqlState: SSDupKey,
sqlState: SSConstraintViolation,
text: "invalid argument with errno",
},
{
err: vterrors.Errorf(
vtrpcpb.Code_DEADLINE_EXCEEDED,
"connection deadline exceeded"),
code: ERQueryInterrupted,
sqlState: SSUnknownSQLState,
sqlState: SSQueryInterrupted,
text: "deadline exceeded",
},
{
err: vterrors.Errorf(
vtrpcpb.Code_RESOURCE_EXHAUSTED,
"query pool timeout"),
code: ERTooManyUserConnections,
sqlState: SSUnknownSQLState,
sqlState: SSClientError,
text: "resource exhausted",
},
{
Expand All @@ -1181,27 +1180,17 @@ func TestErrorCodes(t *testing.T) {
}

for _, test := range tests {
th.SetErr(NewSQLErrorFromError(test.err))
result, err := client.ExecuteFetch("error", 100, false)
if err == nil {
t.Fatalf("mysql should have failed but returned: %v", result)
}
serr, ok := err.(*SQLError)
if !ok {
t.Fatalf("mysql should have returned a SQLError")
}

if serr.Number() != test.code {
t.Errorf("error in %s: want code %v got %v", test.text, test.code, serr.Number())
}

if serr.SQLState() != test.sqlState {
t.Errorf("error in %s: want sqlState %v got %v", test.text, test.sqlState, serr.SQLState())
}

if !strings.Contains(serr.Error(), test.err.Error()) {
t.Errorf("error in %s: want err %v got %v", test.text, test.err.Error(), serr.Error())
}
t.Run(test.err.Error(), func(t *testing.T) {
th.SetErr(NewSQLErrorFromError(test.err))
rs, err := client.ExecuteFetch("error", 100, false)
require.Error(t, err, "mysql should have failed but returned: %v", rs)
serr, ok := err.(*SQLError)
require.True(t, ok, "mysql should have returned a SQLError")

assert.Equal(t, test.code, serr.Number(), "error in %s: want code %v got %v", test.text, test.code, serr.Number())
assert.Equal(t, test.sqlState, serr.SQLState(), "error in %s: want sqlState %v got %v", test.text, test.sqlState, serr.SQLState())
assert.Contains(t, serr.Error(), test.err.Error())
})
}
}

Expand Down Expand Up @@ -1349,7 +1338,7 @@ func TestListenerShutdown(t *testing.T) {
if sqlErr.Number() != ERServerShutdown {
t.Fatalf("Unexpected sql error code: %d", sqlErr.Number())
}
if sqlErr.SQLState() != SSServerShutdown {
if sqlErr.SQLState() != SSNetError {
t.Fatalf("Unexpected error sql state: %s", sqlErr.SQLState())
}
if sqlErr.Message != "Server shutdown in progress" {
Expand Down
97 changes: 67 additions & 30 deletions go/mysql/sql_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,56 +91,43 @@ func NewSQLErrorFromError(err error) error {
return serr
}

sErr := convertToMysqlError(err)
if _, ok := sErr.(*SQLError); ok {
return sErr
}

msg := err.Error()
match := errExtract.FindStringSubmatch(msg)
if len(match) < 2 {
// Map vitess error codes into the mysql equivalent
code := vterrors.Code(err)
num := ERUnknownError
ss := SSUnknownSQLState
switch code {
case vtrpcpb.Code_CANCELED:
num = ERQueryInterrupted
case vtrpcpb.Code_UNKNOWN:
num = ERUnknownError
case vtrpcpb.Code_INVALID_ARGUMENT:
// TODO/demmer there are several more appropriate mysql error
// codes for the various invalid argument cases.
// it would be better to change the call sites to use
// the mysql style "(errno X) (sqlstate Y)" format rather than
// trying to add vitess error codes for all these cases
num = ERUnknownError
case vtrpcpb.Code_DEADLINE_EXCEEDED:
case vtrpcpb.Code_CANCELED, vtrpcpb.Code_DEADLINE_EXCEEDED, vtrpcpb.Code_ABORTED:
num = ERQueryInterrupted
case vtrpcpb.Code_NOT_FOUND:
num = ERUnknownError
case vtrpcpb.Code_ALREADY_EXISTS:
ss = SSQueryInterrupted
case vtrpcpb.Code_UNKNOWN, vtrpcpb.Code_INVALID_ARGUMENT, vtrpcpb.Code_NOT_FOUND, vtrpcpb.Code_ALREADY_EXISTS,
vtrpcpb.Code_FAILED_PRECONDITION, vtrpcpb.Code_OUT_OF_RANGE, vtrpcpb.Code_UNAVAILABLE, vtrpcpb.Code_DATA_LOSS:
num = ERUnknownError
case vtrpcpb.Code_PERMISSION_DENIED:
num = ERAccessDeniedError
case vtrpcpb.Code_UNAUTHENTICATED:
case vtrpcpb.Code_PERMISSION_DENIED, vtrpcpb.Code_UNAUTHENTICATED:
num = ERAccessDeniedError
ss = SSAccessDeniedError
case vtrpcpb.Code_RESOURCE_EXHAUSTED:
num = demuxResourceExhaustedErrors(err.Error())
case vtrpcpb.Code_FAILED_PRECONDITION:
num = ERUnknownError
case vtrpcpb.Code_ABORTED:
num = ERQueryInterrupted
case vtrpcpb.Code_OUT_OF_RANGE:
num = ERUnknownError
ss = SSClientError
case vtrpcpb.Code_UNIMPLEMENTED:
num = ERNotSupportedYet
ss = SSClientError
case vtrpcpb.Code_INTERNAL:
num = ERUnknownError
case vtrpcpb.Code_UNAVAILABLE:
num = ERUnknownError
case vtrpcpb.Code_DATA_LOSS:
num = ERUnknownError
num = ERInternalError
ss = SSUnknownSQLState
}

// Not found, build a generic SQLError.
return &SQLError{
Num: num,
State: SSUnknownSQLState,
State: ss,
Message: msg,
}
}
Expand All @@ -162,6 +149,56 @@ func NewSQLErrorFromError(err error) error {
return serr
}

var stateToMysqlCode = map[vterrors.State]struct {
num int
state string
}{
vterrors.Undefined: {num: ERUnknownError, state: SSUnknownSQLState},
vterrors.AccessDeniedError: {num: ERAccessDeniedError, state: SSAccessDeniedError},
vterrors.BadDb: {num: ERBadDb, state: SSClientError},
vterrors.BadFieldError: {num: ERBadFieldError, state: SSBadFieldError},
vterrors.CantUseOptionHere: {num: ERCantUseOptionHere, state: SSClientError},
vterrors.DataOutOfRange: {num: ERDataOutOfRange, state: SSDataOutOfRange},
vterrors.DbCreateExists: {num: ERDbCreateExists, state: SSUnknownSQLState},
vterrors.DbDropExists: {num: ERDbDropExists, state: SSUnknownSQLState},
vterrors.InnodbReadOnly: {num: ERInnodbReadOnly, state: SSUnknownSQLState},
vterrors.LockOrActiveTransaction: {num: ERLockOrActiveTransaction, state: SSUnknownSQLState},
vterrors.NoDB: {num: ERNoDb, state: SSNoDB},
vterrors.NoSuchTable: {num: ERNoSuchTable, state: SSUnknownTable},
vterrors.NotSupportedYet: {num: ERNotSupportedYet, state: SSClientError},
vterrors.ForbidSchemaChange: {num: ERForbidSchemaChange, state: SSUnknownSQLState},
vterrors.NetPacketTooLarge: {num: ERNetPacketTooLarge, state: SSNetError},
vterrors.NonUniqTable: {num: ERNonUniqTable, state: SSClientError},
vterrors.QueryInterrupted: {num: ERQueryInterrupted, state: SSQueryInterrupted},
vterrors.SPDoesNotExist: {num: ERSPDoesNotExist, state: SSClientError},
vterrors.SyntaxError: {num: ERSyntaxError, state: SSClientError},
vterrors.UnsupportedPS: {num: ERUnsupportedPS, state: SSUnknownSQLState},
vterrors.UnknownSystemVariable: {num: ERUnknownSystemVariable, state: SSUnknownSQLState},
vterrors.UnknownTable: {num: ERUnknownTable, state: SSUnknownTable},
vterrors.WrongGroupField: {num: ERWrongGroupField, state: SSClientError},
vterrors.WrongNumberOfColumnsInSelect: {num: ERWrongNumberOfColumnsInSelect, state: SSWrongNumberOfColumns},
vterrors.WrongTypeForVar: {num: ERWrongTypeForVar, state: SSClientError},
vterrors.WrongValueForVar: {num: ERWrongValueForVar, state: SSClientError},
}

func init() {
if len(stateToMysqlCode) != int(vterrors.NumOfStates) {
panic("all vterrors states are not mapped to mysql errors")
}
}

func convertToMysqlError(err error) error {
errState := vterrors.ErrState(err)
if errState == vterrors.Undefined {
return err
}
mysqlCode, ok := stateToMysqlCode[errState]
if !ok {
return err
}
return NewSQLError(mysqlCode.num, mysqlCode.state, err.Error())
}

var isGRPCOverflowRE = regexp.MustCompile(`.*grpc: received message larger than max \(\d+ vs. \d+\)`)

func demuxResourceExhaustedErrors(msg string) int {
Expand Down
Loading

0 comments on commit 451279d

Please sign in to comment.