-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add strict mode #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add strict mode #58
Changes from all commits
3f35029
df0318e
50b25b5
9d1f8ef
ae3b4e8
c4a6e95
8decd5c
68de1a8
26d3f18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,12 @@ | |
|
||
package mysql | ||
|
||
import "errors" | ||
import ( | ||
"database/sql/driver" | ||
"errors" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
var ( | ||
errMalformPkt = errors.New("Malformed Packet") | ||
|
@@ -18,3 +23,82 @@ var ( | |
errOldPassword = errors.New("It seems like you are using old_passwords, which is unsupported. See https://github.com/go-sql-driver/mysql/wiki/old_passwords") | ||
errPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.") | ||
) | ||
|
||
// error type which represents a single MySQL error | ||
type MySQLError struct { | ||
Number uint16 | ||
Message string | ||
} | ||
|
||
func (me *MySQLError) Error() string { | ||
return fmt.Sprintf("Error %d: %s", me.Number, me.Message) | ||
} | ||
|
||
// error type which represents a group (one ore more) MySQL warnings | ||
type MySQLWarnings []mysqlWarning | ||
|
||
func (mws MySQLWarnings) Error() string { | ||
var msg string | ||
for i, warning := range mws { | ||
if i > 0 { | ||
msg += "\r\n" | ||
} | ||
msg += fmt.Sprintf("%s %s: %s", warning.Level, warning.Code, warning.Message) | ||
} | ||
return msg | ||
} | ||
|
||
// error type which represents a single MySQL warning | ||
type mysqlWarning struct { | ||
Level string | ||
Code string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this a number? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the MySQL spec doesn't say it is a number. In contrast to error numbers, which are transmitted numerical, the warning code is transmitted as text. This is why I named warning Code and error Number different. |
||
Message string | ||
} | ||
|
||
func (mc *mysqlConn) getWarnings() (err error) { | ||
rows, err := mc.Query("SHOW WARNINGS", []driver.Value{}) | ||
if err != nil { | ||
return | ||
} | ||
|
||
var warnings = MySQLWarnings{} | ||
var values = make([]driver.Value, 3) | ||
|
||
var warning mysqlWarning | ||
var raw []byte | ||
var ok bool | ||
|
||
for { | ||
err = rows.Next(values) | ||
switch err { | ||
case nil: | ||
warning = mysqlWarning{} | ||
|
||
if raw, ok = values[0].([]byte); ok { | ||
warning.Level = string(raw) | ||
} else { | ||
warning.Level = fmt.Sprintf("%s", values[0]) | ||
} | ||
if raw, ok = values[1].([]byte); ok { | ||
warning.Code = string(raw) | ||
} else { | ||
warning.Code = fmt.Sprintf("%s", values[1]) | ||
} | ||
if raw, ok = values[2].([]byte); ok { | ||
warning.Message = string(raw) | ||
} else { | ||
warning.Message = fmt.Sprintf("%s", values[0]) | ||
} | ||
|
||
warnings = append(warnings, warning) | ||
|
||
case io.EOF: | ||
return warnings | ||
|
||
default: | ||
rows.Close() | ||
return | ||
} | ||
} | ||
return | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -352,8 +352,7 @@ func (mc *mysqlConn) readResultOK() error { | |
switch data[0] { | ||
|
||
case iOK: | ||
mc.handleOkPacket(data) | ||
return nil | ||
return mc.handleOkPacket(data) | ||
|
||
case iEOF: // someone is using old_passwords | ||
return errOldPassword | ||
|
@@ -373,8 +372,7 @@ func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { | |
switch data[0] { | ||
|
||
case iOK: | ||
mc.handleOkPacket(data) | ||
return 0, nil | ||
return 0, mc.handleOkPacket(data) | ||
|
||
case iERR: | ||
return 0, mc.handleErrorPacket(data) | ||
|
@@ -415,25 +413,39 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error { | |
} | ||
|
||
// Error Message [string] | ||
return fmt.Errorf("Error %d: %s", errno, string(data[pos:])) | ||
return &MySQLError{ | ||
Number: errno, | ||
Message: string(data[pos:]), | ||
} | ||
} | ||
|
||
// Ok Packet | ||
// http://dev.mysql.com/doc/internals/en/overview.html#packet-OK_Packet | ||
func (mc *mysqlConn) handleOkPacket(data []byte) { | ||
var n int | ||
func (mc *mysqlConn) handleOkPacket(data []byte) (err error) { | ||
var n, m int | ||
|
||
// 0x00 [1 byte] | ||
|
||
// Affected rows [Length Coded Binary] | ||
mc.affectedRows, _, n = readLengthEncodedInteger(data[1:]) | ||
|
||
// Insert id [Length Coded Binary] | ||
mc.insertId, _, _ = readLengthEncodedInteger(data[1+n:]) | ||
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:]) | ||
|
||
// server_status [2 bytes] | ||
|
||
// warning count [2 bytes] | ||
if !mc.strict { | ||
return | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not |
||
pos := 1 + n + m + 2 | ||
if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { | ||
err = mc.getWarnings() | ||
} | ||
} | ||
|
||
// message [until end of packet] | ||
return | ||
} | ||
|
||
// Read Packets as Field Packets until EOF-Packet or an Error appears | ||
|
@@ -625,7 +637,13 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (columnCount uint16, err error) | |
pos += 2 | ||
|
||
// Warning count [16 bit uint] | ||
// bytesToUint16(data[pos : pos+2]) | ||
if !stmt.mc.strict { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like L440: drop branch - but I guess you write it this way because of benchmarks and analysis of disassembled code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it's mirco optimization... I should really stop with that. |
||
return | ||
} else { | ||
if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { | ||
err = stmt.mc.getWarnings() | ||
} | ||
} | ||
} | ||
return | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've already got MySQLError - why not make two error types and get rid of the
errors
import?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the benefit from that. We import the errors package elsewhere and it is very tiny anyways: http://golang.org/src/pkg/errors/errors.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but getting rid of
errors
is not the main benefit. I think a custom error type could be used as a basis for errors and warnings. And you could make the errorsconst
instead ofvar
.