diff --git a/connection.go b/connection.go index bc70ca775..36120604b 100644 --- a/connection.go +++ b/connection.go @@ -417,7 +417,7 @@ func (mc *mysqlConn) readIgnoreColumns(rows *textRows, resLen int) (*textRows, e rows.rs.columnNames = make([]string, resLen) return rows, nil } - return nil, ErrOptionalResultSet + return nil, ErrOptionalResultSetPkt } // Gets the value of the given MySQL System Variable diff --git a/connector.go b/connector.go index 666f088de..39d630c11 100644 --- a/connector.go +++ b/connector.go @@ -143,7 +143,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { mc.resultSetMetadata = resultSetMetadataFull default: mc.Close() - return nil, ErrOptionalResultSet + return nil, ErrOptionalResultSetPkt } } diff --git a/driver_test.go b/driver_test.go index fbdfff0ea..5ab77bce7 100644 --- a/driver_test.go +++ b/driver_test.go @@ -202,6 +202,7 @@ func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) func maybeSkip(t *testing.T, err error, skipErrno uint16) { mySQLErr, ok := err.(*MySQLError) if !ok { + errLog.Print("non match") return } @@ -1348,9 +1349,11 @@ func TestFoundRows(t *testing.T) { func TestOptionalResultSetMetadata(t *testing.T) { runTests(t, dsn+"&resultSetMetadata=none", func(dbt *DBTest) { _, err := dbt.db.Exec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") - // Error 1193: Unknown system variable 'resultset_metadata' => skip test, - // MySQL server version is too old - maybeSkip(t, err, 1193) + if err == ErrNoOptionalResultSet { + t.Skip("server does not support resultset metadata") + } else if err != nil { + dbt.Fatal(err) + } dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") row := dbt.db.QueryRow("SELECT id, data FROM test WHERE id = 1") @@ -1366,9 +1369,11 @@ func TestOptionalResultSetMetadata(t *testing.T) { }) runTests(t, dsn+"&resultSetMetadata=full", func(dbt *DBTest) { _, err := dbt.db.Exec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") - // Error 1193: Unknown system variable 'resultset_metadata' => skip test, - // MySQL server version is too old - maybeSkip(t, err, 1193) + if err == ErrNoOptionalResultSet { + t.Skip("server does not support resultset metadata") + } else if err != nil { + dbt.Fatal(err) + } dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") row := dbt.db.QueryRow("SELECT id, data FROM test WHERE id = 1") diff --git a/errors.go b/errors.go index a2b027c1a..4a5032bb7 100644 --- a/errors.go +++ b/errors.go @@ -17,19 +17,20 @@ import ( // Various errors the driver might return. Can change between driver versions. var ( - ErrInvalidConn = errors.New("invalid connection") - ErrMalformPkt = errors.New("malformed packet") - ErrNoTLS = errors.New("TLS requested but server does not support TLS") - ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN") - ErrNativePassword = errors.New("this user requires mysql native password authentication") - ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") - ErrUnknownPlugin = errors.New("this authentication plugin is not supported") - ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") - ErrPktSync = errors.New("commands out of sync. You can't run this command now") - ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") - ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") - ErrBusyBuffer = errors.New("busy buffer") - ErrOptionalResultSet = errors.New("malformed optional resultset metadata packets") + ErrInvalidConn = errors.New("invalid connection") + ErrMalformPkt = errors.New("malformed packet") + ErrNoTLS = errors.New("TLS requested but server does not support TLS") + ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN") + ErrNativePassword = errors.New("this user requires mysql native password authentication") + ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") + ErrUnknownPlugin = errors.New("this authentication plugin is not supported") + ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") + ErrPktSync = errors.New("commands out of sync. You can't run this command now") + ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") + ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") + ErrBusyBuffer = errors.New("busy buffer") + ErrNoOptionalResultSet = errors.New("requested optional resultset metadata but server does not support") + ErrOptionalResultSetPkt = errors.New("malformed optional resultset metadata packets") // errBadConnNoWrite is used for connection errors where nothing was sent to the database yet. // If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn diff --git a/packets.go b/packets.go index 9c466e2a6..222368985 100644 --- a/packets.go +++ b/packets.go @@ -235,10 +235,18 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro if len(data) > pos { // character set [1 byte] // status flags [2 bytes] + pos += 1 + 2 // capability flags (upper 2 bytes) [2 bytes] + upperFlags := clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) + mc.flags |= upperFlags << 16 + pos += 2 + if mc.flags&clientOptionalResultSetMetadata == 0 && mc.cfg.ResultSetMetadata != "" { + return nil, "", ErrNoOptionalResultSet + } + // length of auth-plugin-data [1 byte] // reserved (all [00]) [10 bytes] - pos += 1 + 2 + 2 + 1 + 10 + pos += 1 + 10 // second part of the password cipher [mininum 13 bytes], // where len=MAX(13, length of auth-plugin-data - 8) @@ -562,10 +570,10 @@ func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { // Sniff one extra byte for resultset metadata if we set capability // CLIENT_OPTIONAL_RESULTSET_METADTA // https://dev.mysql.com/worklog/task/?id=8134 - if len(data) == 2 { + if len(data) == 2 && mc.flags&clientOptionalResultSetMetadata != 0 { // ResultSet metadata flag check if mc.resultSetMetadata != data[1] { - return 0, ErrOptionalResultSet + return 0, ErrOptionalResultSetPkt } return int(num), nil }