Merge pull request #618 from youtube/mysql_flags

Pass "Flags" from MySQL field from the result set e.g. to preserve if integers are unsigned.
This commit is contained in:
Michael Berlin 2015-04-27 17:16:56 -07:00
Родитель 08be00d4f5 68ddcf73ae
Коммит 92a7984ce2
10 изменённых файлов: 48 добавлений и 46 удалений

Просмотреть файл

@ -200,6 +200,7 @@ func (conn *Connection) Fields() (fields []proto.Field) {
fname := (*[maxSize]byte)(unsafe.Pointer(cfields[i].name))[:length]
fields[i].Name = string(fname)
fields[i].Type = int64(cfields[i]._type)
fields[i].Flags = int64(cfields[i].flags)
}
return fields
}

Просмотреть файл

@ -26,9 +26,9 @@ type extraQueryResult struct {
}
func TestQueryResult(t *testing.T) {
want := "\x85\x00\x00\x00\x04Fields\x00*\x00\x00\x00\x030\x00\"\x00\x00\x00\x05Name\x00\x04\x00\x00\x00\x00name\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x02\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00 \x00\x00\x00\x040\x00\x18\x00\x00\x00\x050\x00\x01\x00\x00\x00\x001\x051\x00\x02\x00\x00\x00\x00aa\x00\x00\x00"
want := "\x94\x00\x00\x00\x04Fields\x009\x00\x00\x00\x030\x001\x00\x00\x00\x05Name\x00\x04\x00\x00\x00\x00name\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x12Flags\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x02\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00 \x00\x00\x00\x040\x00\x18\x00\x00\x00\x050\x00\x01\x00\x00\x00\x001\x051\x00\x02\x00\x00\x00\x00aa\x00\x00\x00"
custom := QueryResult{
Fields: []Field{{"name", 1}},
Fields: []Field{{"name", 1, VT_ZEROVALUE_FLAG}},
RowsAffected: 2,
InsertId: 3,
Rows: [][]sqltypes.Value{

Просмотреть файл

@ -21,6 +21,7 @@ func (field *Field) MarshalBson(buf *bytes2.ChunkedWriter, key string) {
bson.EncodeString(buf, "Name", field.Name)
bson.EncodeInt64(buf, "Type", field.Type)
bson.EncodeInt64(buf, "Flags", field.Flags)
lenWriter.Close()
}
@ -43,6 +44,8 @@ func (field *Field) UnmarshalBson(buf *bytes.Buffer, kind byte) {
field.Name = bson.DecodeString(buf, kind)
case "Type":
field.Type = bson.DecodeInt64(buf, kind)
case "Flags":
field.Flags = bson.DecodeInt64(buf, kind)
default:
bson.Skip(buf, kind)
}

Просмотреть файл

@ -29,7 +29,7 @@ var testcases = []TestCase{
{Name: "foo", Type: 1},
},
},
encoded: "i\x00\x00\x00\x04Fields\x00)\x00\x00\x00\x030\x00!\x00\x00\x00\x05Name\x00\x03\x00\x00\x00\x00foo\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x00\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00\x05\x00\x00\x00\x00\x00",
encoded: "x\x00\x00\x00\x04Fields\x008\x00\x00\x00\x030\x000\x00\x00\x00\x05Name\x00\x03\x00\x00\x00\x00foo\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x12Flags\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x00\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00\x05\x00\x00\x00\x00\x00",
},
// Only rows, no fields
{

Просмотреть файл

@ -41,10 +41,35 @@ const (
VT_GEOMETRY = 255
)
// MySQL field flags bitset values e.g. to distinguish between signed and unsigned integer.
// Comments are taken from the original source code.
// These numbers should exactly match values defined in dist/mysql-5.1.52/include/mysql_com.h
const (
// VT_ZEROVALUE_FLAG is not part of the MySQL specification and only used in unit tests.
VT_ZEROVALUE_FLAG = 0
VT_NOT_NULL_FLAG = 1 /* Field can't be NULL */
VT_PRI_KEY_FLAG = 2 /* Field is part of a primary key */
VT_UNIQUE_KEY_FLAG = 4 /* Field is part of a unique key */
VT_MULTIPLE_KEY_FLAG = 8 /* Field is part of a key */
VT_BLOB_FLAG = 16 /* Field is a blob */
VT_UNSIGNED_FLAG = 32 /* Field is unsigned */
VT_ZEROFILL_FLAG = 64 /* Field is zerofill */
VT_BINARY_FLAG = 128 /* Field is binary */
/* The following are only sent to new clients */
VT_ENUM_FLAG = 256 /* field is an enum */
VT_AUTO_INCREMENT_FLAG = 512 /* field is a autoincrement field */
VT_TIMESTAMP_FLAG = 1024 /* Field is a timestamp */
VT_SET_FLAG = 2048 /* field is a set */
VT_NO_DEFAULT_VALUE_FLAG = 4096 /* Field doesn't have default value */
VT_ON_UPDATE_NOW_FLAG = 8192 /* Field is set to NOW on UPDATE */
VT_NUM_FLAG = 32768 /* Field is num (for clients) */
)
// Field describes a column returned by MySQL.
type Field struct {
Name string
Type int64
Name string
Type int64
Flags int64
}
//go:generate bsongen -file $GOFILE -type Field -o field_bson.go
@ -77,6 +102,7 @@ type Charset struct {
// - int64 if possible, otherwise, uint64
// - float64 for floating point values that fit in a float
// - []byte for everything else
// TODO(mberlin): Make this a method of "Field" and consider VT_UNSIGNED_FLAG in "Flags" as well.
func Convert(mysqlType int64, val sqltypes.Value) (interface{}, error) {
if val.IsNull() {
return nil, nil

Просмотреть файл

@ -203,6 +203,7 @@ type Result struct {
err error
}
// TODO(mberlin): Populate flags here as well (e.g. to correctly identify unsigned integer type)?
func NewResult(rowCount, rowsAffected, insertId int64, fields []mproto.Field) *Result {
return &Result{
qr: &mproto.QueryResult{

Просмотреть файл

@ -165,40 +165,11 @@ func TestQueryShard(t *testing.T) {
func TestQueryResult(t *testing.T) {
// We can't do the reflection test because bson
// doesn't do it correctly for embedded fields.
want := "|\x01\x00\x00" +
"\x03Result\x00\x85\x00\x00\x00" +
"\x04Fields\x00*\x00\x00\x00" +
"\x030\x00\"\x00\x00\x00" +
"\x05Name\x00\x04\x00\x00\x00\x00name" +
"\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"?RowsAffected\x00\x02\x00\x00\x00\x00\x00\x00\x00" +
"?InsertId\x00\x03\x00\x00\x00\x00\x00\x00\x00" +
"\x04Rows\x00 \x00\x00\x00" +
"\x040\x00\x18\x00\x00\x00" +
"\x050\x00\x01\x00\x00\x00" +
"\x001\x051\x00\x02\x00\x00\x00\x00aa" +
"\x00\x00\x00" +
"\x03Session\x00\xd0\x00\x00\x00" +
"\bInTransaction\x00\x01" +
"\x04ShardSessions\x00\xac\x00\x00\x00" +
"\x030\x00Q\x00\x00\x00" +
"\x05Keyspace\x00\x01\x00\x00\x00\x00a" +
"\x05Shard\x00\x01\x00\x00\x00\x000" +
"\x05TabletType\x00\a\x00\x00\x00\x00replica" +
"\x12TransactionId\x00\x01\x00\x00\x00\x00\x00\x00\x00" +
"\x00" +
"\x031\x00P\x00\x00\x00" +
"\x05Keyspace\x00\x01\x00\x00\x00\x00b" +
"\x05Shard\x00\x01\x00\x00\x00\x001" +
"\x05TabletType\x00\x06\x00\x00\x00\x00master" +
"\x12TransactionId\x00\x02\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00" +
"\x05Error\x00\x05\x00\x00\x00\x00error" +
"\x00"
want := "\x8b\x01\x00\x00\x03Result\x00\x94\x00\x00\x00\x04Fields\x009\x00\x00\x00\x030\x001\x00\x00\x00\x05Name\x00\x04\x00\x00\x00\x00name\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x12Flags\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x02\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00 \x00\x00\x00\x040\x00\x18\x00\x00\x00\x050\x00\x01\x00\x00\x00\x001\x051\x00\x02\x00\x00\x00\x00aa\x00\x00\x00\x03Session\x00\xd0\x00\x00\x00\bInTransaction\x00\x01\x04ShardSessions\x00\xac\x00\x00\x00\x030\x00Q\x00\x00\x00\x05Keyspace\x00\x01\x00\x00\x00\x00a\x05Shard\x00\x01\x00\x00\x00\x000\x05TabletType\x00\a\x00\x00\x00\x00replica\x12TransactionId\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x031\x00P\x00\x00\x00\x05Keyspace\x00\x01\x00\x00\x00\x00b\x05Shard\x00\x01\x00\x00\x00\x001\x05TabletType\x00\x06\x00\x00\x00\x00master\x12TransactionId\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05Error\x00\x05\x00\x00\x00\x00error\x00"
custom := QueryResult{
Result: &mproto.QueryResult{
Fields: []mproto.Field{{"name", 1}},
Fields: []mproto.Field{{"name", 1, mproto.VT_ZEROVALUE_FLAG}},
RowsAffected: 2,
InsertId: 3,
Rows: [][]sqltypes.Value{
@ -214,7 +185,7 @@ func TestQueryResult(t *testing.T) {
}
got := string(encoded)
if want != got {
t.Errorf("want\n%+v, got\n%+v", want, got)
t.Errorf("want\n%#v, got\n%#v", want, got)
}
var unmarshalled QueryResult
@ -353,7 +324,7 @@ type extraQueryResultList struct {
func TestQueryResultList(t *testing.T) {
reflected, err := bson.Marshal(&reflectQueryResultList{
List: []mproto.QueryResult{{
Fields: []mproto.Field{{"name", 1}},
Fields: []mproto.Field{{"name", 1, mproto.VT_ZEROVALUE_FLAG}},
RowsAffected: 2,
InsertId: 3,
Rows: [][]sqltypes.Value{
@ -370,7 +341,7 @@ func TestQueryResultList(t *testing.T) {
custom := QueryResultList{
List: []mproto.QueryResult{{
Fields: []mproto.Field{{"name", 1}},
Fields: []mproto.Field{{"name", 1, mproto.VT_ZEROVALUE_FLAG}},
RowsAffected: 2,
InsertId: 3,
Rows: [][]sqltypes.Value{

Просмотреть файл

@ -120,8 +120,8 @@ func TestDeleteEqual(t *testing.T) {
sbc.setResults([]*mproto.QueryResult{&mproto.QueryResult{
Fields: []mproto.Field{
{"id", 3},
{"name", 253},
{"id", 3, mproto.VT_ZEROVALUE_FLAG},
{"name", 253, mproto.VT_ZEROVALUE_FLAG},
},
RowsAffected: 1,
InsertId: 0,
@ -257,8 +257,8 @@ func TestDeleteVindexFail(t *testing.T) {
sbc.setResults([]*mproto.QueryResult{&mproto.QueryResult{
Fields: []mproto.Field{
{"id", 3},
{"name", 253},
{"id", 3, mproto.VT_ZEROVALUE_FLAG},
{"name", 253, mproto.VT_ZEROVALUE_FLAG},
},
RowsAffected: 1,
InsertId: 0,

Просмотреть файл

@ -506,8 +506,8 @@ func (sbc *sandboxConn) getNextResult() *mproto.QueryResult {
var singleRowResult = &mproto.QueryResult{
Fields: []mproto.Field{
{"id", 3},
{"value", 253}},
{"id", 3, mproto.VT_ZEROVALUE_FLAG},
{"value", 253, mproto.VT_ZEROVALUE_FLAG}},
RowsAffected: 1,
InsertId: 0,
Rows: [][]sqltypes.Value{{

Просмотреть файл

@ -131,7 +131,7 @@ func TestRowsFail(t *testing.T) {
ri = NewRows(&badResult2)
dest = make([]driver.Value, 1)
err = ri.Next(dest)
want = `conversion error: field: {field1 3}, val: value: strconv.ParseUint: parsing "value": invalid syntax`
want = `conversion error: field: {field1 3 0}, val: value: strconv.ParseUint: parsing "value": invalid syntax`
if err == nil || err.Error() != want {
t.Errorf("Next: %v, want %s", err, want)
}