From 00650f681c529712db815b864fc2a918a282f4bd Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Tue, 17 May 2022 16:04:05 +0300 Subject: [PATCH 1/7] code health: replace deprecated EncodeSliceLen and DecodeSliceLen EncodeSliceLen[1] and DecodeSliceLen[2] are marked as deprecated in msgpack v2.9.2. The patch replaces it with EncodeArrayLen and DecodeArrayLen. 1. https://pkg.go.dev/github.com/vmihailenco/msgpack@v2.9.2+incompatible#Encoder.EncodeSliceLen 2. https://pkg.go.dev/github.com/vmihailenco/msgpack@v2.9.2+incompatible#Decoder.DecodeSliceLen Part of #124 --- client_tools.go | 12 ++++++------ datetime/datetime_test.go | 14 +++++++------- decimal/decimal_test.go | 4 ++-- example_custom_unpacking_test.go | 6 +++--- queue/queue.go | 2 +- queue/queue_test.go | 4 ++-- queue/task.go | 2 +- request.go | 10 +++++----- tarantool_test.go | 4 ++-- uuid/uuid_test.go | 2 +- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/client_tools.go b/client_tools.go index 27976a9a7..20bfa722f 100644 --- a/client_tools.go +++ b/client_tools.go @@ -11,7 +11,7 @@ type IntKey struct { } func (k IntKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(1) + enc.EncodeArrayLen(1) enc.EncodeInt(k.I) return nil } @@ -23,7 +23,7 @@ type UintKey struct { } func (k UintKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(1) + enc.EncodeArrayLen(1) enc.EncodeUint(k.I) return nil } @@ -35,7 +35,7 @@ type StringKey struct { } func (k StringKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(1) + enc.EncodeArrayLen(1) enc.EncodeString(k.S) return nil } @@ -47,7 +47,7 @@ type IntIntKey struct { } func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(2) + enc.EncodeArrayLen(2) enc.EncodeInt(k.I1) enc.EncodeInt(k.I2) return nil @@ -61,7 +61,7 @@ type Op struct { } func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(3) + enc.EncodeArrayLen(3) enc.EncodeString(o.Op) enc.EncodeInt(o.Field) return enc.Encode(o.Arg) @@ -149,7 +149,7 @@ type OpSplice struct { } func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(5) + enc.EncodeArrayLen(5) enc.EncodeString(o.Op) enc.EncodeInt(o.Field) enc.EncodeInt(o.Pos) diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 93d292ecd..2de8888d7 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -271,7 +271,7 @@ type Tuple1 struct { } func (t *Tuple1) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { + if err := e.EncodeArrayLen(2); err != nil { return err } if err := e.Encode(&t.Datetime); err != nil { @@ -283,7 +283,7 @@ func (t *Tuple1) EncodeMsgpack(e *msgpack.Encoder) error { func (t *Tuple1) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { @@ -297,7 +297,7 @@ func (t *Tuple1) DecodeMsgpack(d *msgpack.Decoder) error { } func (ev *Event) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { + if err := e.EncodeArrayLen(2); err != nil { return err } if err := e.EncodeString(ev.Location); err != nil { @@ -312,7 +312,7 @@ func (ev *Event) EncodeMsgpack(e *msgpack.Encoder) error { func (ev *Event) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 2 { @@ -330,7 +330,7 @@ func (ev *Event) DecodeMsgpack(d *msgpack.Decoder) error { } func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(3); err != nil { + if err := e.EncodeArrayLen(3); err != nil { return err } if err := e.EncodeUint(c.Cid); err != nil { @@ -346,7 +346,7 @@ func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 3 { @@ -358,7 +358,7 @@ func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { if c.Orig, err = d.DecodeString(); err != nil { return err } - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } c.Events = make([]Event, l) diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go index 1c395b393..499d939c2 100644 --- a/decimal/decimal_test.go +++ b/decimal/decimal_test.go @@ -41,7 +41,7 @@ type TupleDecimal struct { } func (t *TupleDecimal) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(1); err != nil { + if err := e.EncodeArrayLen(1); err != nil { return err } return e.EncodeValue(reflect.ValueOf(&t.number)) @@ -50,7 +50,7 @@ func (t *TupleDecimal) EncodeMsgpack(e *msgpack.Encoder) error { func (t *TupleDecimal) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index 2910010cd..d524dfde8 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -25,7 +25,7 @@ type Tuple3 struct { } func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(3); err != nil { + if err := e.EncodeArrayLen(3); err != nil { return err } if err := e.EncodeUint(c.Cid); err != nil { @@ -41,7 +41,7 @@ func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 3 { @@ -53,7 +53,7 @@ func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { if c.Orig, err = d.DecodeString(); err != nil { return err } - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } c.Members = make([]Member, l) diff --git a/queue/queue.go b/queue/queue.go index 8a3e3e17d..68a98672b 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -339,7 +339,7 @@ type queueData struct { func (qd *queueData) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l > 1 { diff --git a/queue/queue_test.go b/queue/queue_test.go index 48fb71257..25af17b12 100644 --- a/queue/queue_test.go +++ b/queue/queue_test.go @@ -186,7 +186,7 @@ type customData struct { func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { @@ -199,7 +199,7 @@ func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { } func (c *customData) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(1); err != nil { + if err := e.EncodeArrayLen(1); err != nil { return err } if err := e.EncodeString(c.customField); err != nil { diff --git a/queue/task.go b/queue/task.go index fc16ffb88..add05efd6 100644 --- a/queue/task.go +++ b/queue/task.go @@ -17,7 +17,7 @@ type Task struct { func (t *Task) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l < 3 { diff --git a/request.go b/request.go index c708b79b4..a892ca678 100644 --- a/request.go +++ b/request.go @@ -200,7 +200,7 @@ type single struct { func (s *single) DecodeMsgpack(d *msgpack.Decoder) error { var err error var len int - if len, err = d.DecodeSliceLen(); err != nil { + if len, err = d.DecodeArrayLen(); err != nil { return err } if s.found = len >= 1; !s.found { @@ -433,7 +433,7 @@ func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error { } encodeNamedFromMap := func(mp map[string]interface{}) error { - if err := enc.EncodeSliceLen(len(mp)); err != nil { + if err := enc.EncodeArrayLen(len(mp)); err != nil { return err } for k, v := range mp { @@ -445,7 +445,7 @@ func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error { } encodeNamedFromStruct := func(val reflect.Value) error { - if err := enc.EncodeSliceLen(val.NumField()); err != nil { + if err := enc.EncodeArrayLen(val.NumField()); err != nil { return err } cached, ok := lowerCaseNames.Load(val.Type()) @@ -479,7 +479,7 @@ func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error { if !ok { castedKVSlice := from.([]KeyValueBind) t := len(castedKVSlice) - if err := enc.EncodeSliceLen(t); err != nil { + if err := enc.EncodeArrayLen(t); err != nil { return err } for _, v := range castedKVSlice { @@ -490,7 +490,7 @@ func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error { return nil } - if err := enc.EncodeSliceLen(len(castedSlice)); err != nil { + if err := enc.EncodeArrayLen(len(castedSlice)); err != nil { return err } for i := 0; i < len(castedSlice); i++ { diff --git a/tarantool_test.go b/tarantool_test.go index de4062c9e..f3fdbb787 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -27,7 +27,7 @@ type Member struct { } func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { + if err := e.EncodeArrayLen(2); err != nil { return err } if err := e.EncodeString(m.Name); err != nil { @@ -42,7 +42,7 @@ func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { func (m *Member) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 2 { diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index 554dadbe1..aed760009 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -36,7 +36,7 @@ type TupleUUID struct { func (t *TupleUUID) DecodeMsgpack(d *msgpack.Decoder) error { var err error var l int - if l, err = d.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { From 3507ee30c335d597aeb65e9d8dcb85a3440195b9 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Tue, 17 May 2022 11:06:12 +0300 Subject: [PATCH 2/7] code health: replace EncodeUint64 by EncodeUint We use everywhere EncodeInt instead of EncodeInt64. Also we use everywhere EncodeUint64 instead of EncodeUint. It can be confusing. Although EncodeUint64 and EncodeUint have same logic in msgpack.v2, but different in msgpack.v5. It's good for migration to msgpack.v5 too. Part of #124. --- prepared.go | 12 ++++++------ request.go | 50 +++++++++++++++++++++++++------------------------- stream.go | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/prepared.go b/prepared.go index 0f9303344..2546d34b6 100644 --- a/prepared.go +++ b/prepared.go @@ -22,21 +22,21 @@ type Prepared struct { func fillPrepare(enc *msgpack.Encoder, expr string) error { enc.EncodeMapLen(1) - enc.EncodeUint64(KeySQLText) + enc.EncodeUint(KeySQLText) return enc.EncodeString(expr) } func fillUnprepare(enc *msgpack.Encoder, stmt Prepared) error { enc.EncodeMapLen(1) - enc.EncodeUint64(KeyStmtID) - return enc.EncodeUint64(uint64(stmt.StatementID)) + enc.EncodeUint(KeyStmtID) + return enc.EncodeUint(uint(stmt.StatementID)) } func fillExecutePrepared(enc *msgpack.Encoder, stmt Prepared, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeyStmtID) - enc.EncodeUint64(uint64(stmt.StatementID)) - enc.EncodeUint64(KeySQLBind) + enc.EncodeUint(KeyStmtID) + enc.EncodeUint(uint(stmt.StatementID)) + enc.EncodeUint(KeySQLBind) return encodeSQLBind(enc, args) } diff --git a/request.go b/request.go index a892ca678..7d14d3710 100644 --- a/request.go +++ b/request.go @@ -11,28 +11,28 @@ import ( ) func fillSearch(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) error { - enc.EncodeUint64(KeySpaceNo) - enc.EncodeUint64(uint64(spaceNo)) - enc.EncodeUint64(KeyIndexNo) - enc.EncodeUint64(uint64(indexNo)) - enc.EncodeUint64(KeyKey) + enc.EncodeUint(KeySpaceNo) + enc.EncodeUint(uint(spaceNo)) + enc.EncodeUint(KeyIndexNo) + enc.EncodeUint(uint(indexNo)) + enc.EncodeUint(KeyKey) return enc.Encode(key) } func fillIterator(enc *msgpack.Encoder, offset, limit, iterator uint32) { - enc.EncodeUint64(KeyIterator) - enc.EncodeUint64(uint64(iterator)) - enc.EncodeUint64(KeyOffset) - enc.EncodeUint64(uint64(offset)) - enc.EncodeUint64(KeyLimit) - enc.EncodeUint64(uint64(limit)) + enc.EncodeUint(KeyIterator) + enc.EncodeUint(uint(iterator)) + enc.EncodeUint(KeyOffset) + enc.EncodeUint(uint(offset)) + enc.EncodeUint(KeyLimit) + enc.EncodeUint(uint(limit)) } func fillInsert(enc *msgpack.Encoder, spaceNo uint32, tuple interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeySpaceNo) - enc.EncodeUint64(uint64(spaceNo)) - enc.EncodeUint64(KeyTuple) + enc.EncodeUint(KeySpaceNo) + enc.EncodeUint(uint(spaceNo)) + enc.EncodeUint(KeyTuple) return enc.Encode(tuple) } @@ -47,19 +47,19 @@ func fillUpdate(enc *msgpack.Encoder, spaceNo, indexNo uint32, key, ops interfac if err := fillSearch(enc, spaceNo, indexNo, key); err != nil { return err } - enc.EncodeUint64(KeyTuple) + enc.EncodeUint(KeyTuple) return enc.Encode(ops) } func fillUpsert(enc *msgpack.Encoder, spaceNo uint32, tuple, ops interface{}) error { enc.EncodeMapLen(3) - enc.EncodeUint64(KeySpaceNo) - enc.EncodeUint64(uint64(spaceNo)) - enc.EncodeUint64(KeyTuple) + enc.EncodeUint(KeySpaceNo) + enc.EncodeUint(uint(spaceNo)) + enc.EncodeUint(KeyTuple) if err := enc.Encode(tuple); err != nil { return err } - enc.EncodeUint64(KeyDefTuple) + enc.EncodeUint(KeyDefTuple) return enc.Encode(ops) } @@ -70,25 +70,25 @@ func fillDelete(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) func fillCall(enc *msgpack.Encoder, functionName string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeyFunctionName) + enc.EncodeUint(KeyFunctionName) enc.EncodeString(functionName) - enc.EncodeUint64(KeyTuple) + enc.EncodeUint(KeyTuple) return enc.Encode(args) } func fillEval(enc *msgpack.Encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeyExpression) + enc.EncodeUint(KeyExpression) enc.EncodeString(expr) - enc.EncodeUint64(KeyTuple) + enc.EncodeUint(KeyTuple) return enc.Encode(args) } func fillExecute(enc *msgpack.Encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeySQLText) + enc.EncodeUint(KeySQLText) enc.EncodeString(expr) - enc.EncodeUint64(KeySQLBind) + enc.EncodeUint(KeySQLBind) return encodeSQLBind(enc, args) } diff --git a/stream.go b/stream.go index fdfe8408c..62ea0adf0 100644 --- a/stream.go +++ b/stream.go @@ -46,7 +46,7 @@ func fillBegin(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout tim } if hasTimeout { - err = enc.EncodeUint64(KeyTimeout) + err = enc.EncodeUint(KeyTimeout) if err != nil { return err } From c64bd959e19980620a0c597c915b6d2aabf2ad0d Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Tue, 17 May 2022 16:00:56 +0300 Subject: [PATCH 3/7] code health: decode schema with msgpack When we decode the schema with msgpack it allows us to handle errors better. For example, incorrect type conversion leads to a runtime error. Now it will be an usual error that we can handle by our code. Also it will help in migration to msgpack.v5, because an interface decoding rules by default have changed in msgpack.v5. Part of #124 Co-authored-by: Oleg Utkin --- schema.go | 388 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 280 insertions(+), 108 deletions(-) diff --git a/schema.go b/schema.go index b1e8562d2..fc3b0793a 100644 --- a/schema.go +++ b/schema.go @@ -1,7 +1,21 @@ package tarantool import ( + "errors" "fmt" + "gopkg.in/vmihailenco/msgpack.v2" + msgpcode "gopkg.in/vmihailenco/msgpack.v2/codes" +) + +//nolint: varcheck,deadcode +const ( + maxSchemas = 10000 + spaceSpId = 280 + vspaceSpId = 281 + indexSpId = 288 + vindexSpId = 289 + vspaceSpTypeFieldNum = 6 + vspaceSpFormatFieldNum = 7 ) // SchemaResolver is an interface for resolving schema details. @@ -37,19 +51,218 @@ type Space struct { IndexesById map[uint32]*Index } +func isUint(code byte) bool { + return code == msgpcode.Uint8 || code == msgpcode.Uint16 || + code == msgpcode.Uint32 || code == msgpcode.Uint64 || + msgpcode.IsFixedNum(code) +} + +func isMap(code byte) bool { + return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code) +} + +func isArray(code byte) bool { + return code == msgpcode.Array16 || code == msgpcode.Array32 || + msgpcode.IsFixedArray(code) +} + +func isString(code byte) bool { + return msgpcode.IsFixedString(code) || code == msgpcode.Str8 || + code == msgpcode.Str16 || code == msgpcode.Str32 +} + +func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error { + arrayLen, err := d.DecodeArrayLen() + if err != nil { + return err + } + if space.Id, err = d.DecodeUint32(); err != nil { + return err + } + if err := d.Skip(); err != nil { + return err + } + if space.Name, err = d.DecodeString(); err != nil { + return err + } + if space.Engine, err = d.DecodeString(); err != nil { + return err + } + if space.FieldsCount, err = d.DecodeUint32(); err != nil { + return err + } + if arrayLen >= vspaceSpTypeFieldNum { + code, err := d.PeekCode() + if err != nil { + return err + } + if isString(code) { + val, err := d.DecodeString() + if err != nil { + return err + } + space.Temporary = val == "temporary" + } else if isMap(code) { + mapLen, err := d.DecodeMapLen() + if err != nil { + return err + } + for i := 0; i < mapLen; i++ { + key, err := d.DecodeString() + if err != nil { + return err + } + if key == "temporary" { + if space.Temporary, err = d.DecodeBool(); err != nil { + return err + } + } else { + if err = d.Skip(); err != nil { + return err + } + } + } + } else { + return errors.New("unexpected schema format (space flags)") + } + } + space.FieldsById = make(map[uint32]*Field) + space.Fields = make(map[string]*Field) + space.IndexesById = make(map[uint32]*Index) + space.Indexes = make(map[string]*Index) + if arrayLen >= vspaceSpFormatFieldNum { + fieldCount, err := d.DecodeArrayLen() + if err != nil { + return err + } + for i := 0; i < fieldCount; i++ { + field := &Field{} + if err := field.DecodeMsgpack(d); err != nil { + return err + } + field.Id = uint32(i) + space.FieldsById[field.Id] = field + if field.Name != "" { + space.Fields[field.Name] = field + } + } + } + return nil +} + type Field struct { Id uint32 Name string Type string } +func (field *Field) DecodeMsgpack(d *msgpack.Decoder) error { + l, err := d.DecodeMapLen() + if err != nil { + return err + } + for i := 0; i < l; i++ { + key, err := d.DecodeString() + if err != nil { + return err + } + switch key { + case "name": + if field.Name, err = d.DecodeString(); err != nil { + return err + } + case "type": + if field.Type, err = d.DecodeString(); err != nil { + return err + } + default: + if err := d.Skip(); err != nil { + return err + } + } + } + return nil +} + // Index contains information about index. type Index struct { - Id uint32 - Name string - Type string - Unique bool - Fields []*IndexField + Id uint32 + SpaceId uint32 + Name string + Type string + Unique bool + Fields []*IndexField +} + +func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { + _, err := d.DecodeArrayLen() + if err != nil { + return err + } + + if index.SpaceId, err = d.DecodeUint32(); err != nil { + return err + } + if index.Id, err = d.DecodeUint32(); err != nil { + return err + } + if index.Name, err = d.DecodeString(); err != nil { + return err + } + if index.Type, err = d.DecodeString(); err != nil { + return err + } + + var code byte + if code, err = d.PeekCode(); err != nil { + return err + } + + if isUint(code) { + optsUint64, err := d.DecodeUint64() + if err != nil { + return nil + } + index.Unique = optsUint64 > 0 + } else { + var optsMap map[string]interface{} + if err := d.Decode(&optsMap); err != nil { + return fmt.Errorf("unexpected schema format (index flags): %w", err) + } + + var ok bool + if index.Unique, ok = optsMap["unique"].(bool); !ok { + /* see bug https://github.com/tarantool/tarantool/issues/2060 */ + index.Unique = true + } + } + + if code, err = d.PeekCode(); err != nil { + return err + } + + if isUint(code) { + fieldCount, err := d.DecodeUint64() + if err != nil { + return err + } + index.Fields = make([]*IndexField, fieldCount) + for i := 0; i < int(fieldCount); i++ { + index.Fields[i] = new(IndexField) + if index.Fields[i].Id, err = d.DecodeUint32(); err != nil { + return err + } + if index.Fields[i].Type, err = d.DecodeString(); err != nil { + return err + } + } + } else { + if err := d.Decode(&index.Fields); err != nil { + return fmt.Errorf("unexpected schema format (index flags): %w", err) + } + } + + return nil } type IndexField struct { @@ -57,128 +270,87 @@ type IndexField struct { Type string } -//nolint: varcheck,deadcode -const ( - maxSchemas = 10000 - spaceSpId = 280 - vspaceSpId = 281 - indexSpId = 288 - vindexSpId = 289 -) - -func (conn *Connection) loadSchema() (err error) { - var resp *Response - - schema := new(Schema) - schema.SpacesById = make(map[uint32]*Space) - schema.Spaces = make(map[string]*Space) - - // Reload spaces. - resp, err = conn.Select(vspaceSpId, 0, 0, maxSchemas, IterAll, []interface{}{}) +func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { + code, err := d.PeekCode() if err != nil { return err } - for _, row := range resp.Data { - row := row.([]interface{}) - space := new(Space) - space.Id = uint32(row[0].(uint64)) - space.Name = row[2].(string) - space.Engine = row[3].(string) - space.FieldsCount = uint32(row[4].(uint64)) - if len(row) >= 6 { - switch row5 := row[5].(type) { - case string: - space.Temporary = row5 == "temporary" - case map[interface{}]interface{}: - if temp, ok := row5["temporary"]; ok { - space.Temporary = temp.(bool) - } - default: - panic("unexpected schema format (space flags)") - } + + if isMap(code) { + mapLen, err := d.DecodeMapLen() + if err != nil { + return err } - space.FieldsById = make(map[uint32]*Field) - space.Fields = make(map[string]*Field) - space.IndexesById = make(map[uint32]*Index) - space.Indexes = make(map[string]*Index) - if len(row) >= 7 { - for i, f := range row[6].([]interface{}) { - if f == nil { - continue - } - f := f.(map[interface{}]interface{}) - field := new(Field) - field.Id = uint32(i) - if name, ok := f["name"]; ok && name != nil { - field.Name = name.(string) + for i := 0; i < mapLen; i++ { + key, err := d.DecodeString() + if err != nil { + return err + } + switch key { + case "field": + if indexField.Id, err = d.DecodeUint32(); err != nil { + return err } - if type1, ok := f["type"]; ok && type1 != nil { - field.Type = type1.(string) + case "type": + if indexField.Type, err = d.DecodeString(); err != nil { + return err } - space.FieldsById[field.Id] = field - if field.Name != "" { - space.Fields[field.Name] = field + default: + if err := d.Skip(); err != nil { + return err } } } + return nil + } else if isArray(code) { + arrayLen, err := d.DecodeArrayLen() + if err != nil { + return err + } + if indexField.Id, err = d.DecodeUint32(); err != nil { + return err + } + if indexField.Type, err = d.DecodeString(); err != nil { + return err + } + for i := 2; i < arrayLen; i++ { + if err := d.Skip(); err != nil { + return err + } + } + return nil + } + + return errors.New("unexpected schema format (index fields)") +} +func (conn *Connection) loadSchema() (err error) { + schema := new(Schema) + schema.SpacesById = make(map[uint32]*Space) + schema.Spaces = make(map[string]*Space) + + // Reload spaces. + var spaces []*Space + err = conn.SelectTyped(vspaceSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &spaces) + if err != nil { + return err + } + for _, space := range spaces { schema.SpacesById[space.Id] = space schema.Spaces[space.Name] = space } // Reload indexes. - resp, err = conn.Select(vindexSpId, 0, 0, maxSchemas, IterAll, []interface{}{}) + var indexes []*Index + err = conn.SelectTyped(vindexSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &indexes) if err != nil { return err } - for _, row := range resp.Data { - row := row.([]interface{}) - index := new(Index) - index.Id = uint32(row[1].(uint64)) - index.Name = row[2].(string) - index.Type = row[3].(string) - switch row[4].(type) { - case uint64: - index.Unique = row[4].(uint64) > 0 - case map[interface{}]interface{}: - opts := row[4].(map[interface{}]interface{}) - var ok bool - if index.Unique, ok = opts["unique"].(bool); !ok { - /* See bug https://github.com/tarantool/tarantool/issues/2060. */ - index.Unique = true - } - default: - panic("unexpected schema format (index flags)") - } - switch fields := row[5].(type) { - case uint64: - cnt := int(fields) - for i := 0; i < cnt; i++ { - field := new(IndexField) - field.Id = uint32(row[6+i*2].(uint64)) - field.Type = row[7+i*2].(string) - index.Fields = append(index.Fields, field) - } - case []interface{}: - for _, f := range fields { - field := new(IndexField) - switch f := f.(type) { - case []interface{}: - field.Id = uint32(f[0].(uint64)) - field.Type = f[1].(string) - case map[interface{}]interface{}: - field.Id = uint32(f["field"].(uint64)) - field.Type = f["type"].(string) - } - index.Fields = append(index.Fields, field) - } - default: - panic("unexpected schema format (index fields)") - } - spaceId := uint32(row[0].(uint64)) - schema.SpacesById[spaceId].IndexesById[index.Id] = index - schema.SpacesById[spaceId].Indexes[index.Name] = index + for _, index := range indexes { + schema.SpacesById[index.SpaceId].IndexesById[index.Id] = index + schema.SpacesById[index.SpaceId].Indexes[index.Name] = index } + conn.Schema = schema return nil } From 921b893677bcfb987e5509c3013cd85490a79045 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Tue, 2 Aug 2022 16:08:36 +0300 Subject: [PATCH 4/7] code health: extract a msgpack code from the code and tests The patch replaces the msgpack code by internal wrappers. The msgpack usage have been extracted to msgpack.go file for the code and to msgpack_helper_test.go for tests. It is the same logic for submodules. Part of #124 --- client_tools.go | 16 +++----- connection.go | 14 +++---- datetime/datetime.go | 9 ----- datetime/datetime_test.go | 17 ++++----- datetime/msgpack.go | 9 +++++ datetime/msgpack_helper_test.go | 16 ++++++++ decimal/decimal.go | 8 ---- decimal/decimal_test.go | 27 +++++++------- decimal/msgpack.go | 9 +++++ decimal/msgpack_helper_test.go | 16 ++++++++ example_custom_unpacking_test.go | 5 +-- export_test.go | 39 +++++++++++--------- msgpack.go | 39 ++++++++++++++++++++ msgpack_helper_test.go | 8 ++++ prepared.go | 14 +++---- queue/example_msgpack_test.go | 5 +-- queue/msgpack.go | 7 ++++ queue/msgpack_helper_test.go | 8 ++++ queue/queue.go | 3 +- queue/queue_test.go | 5 +-- queue/task.go | 4 +- request.go | 52 +++++++++++++------------- request_test.go | 63 ++++++++++++++++---------------- response.go | 16 ++++---- schema.go | 42 +++++---------------- stream.go | 14 +++---- tarantool_test.go | 5 +-- test_helpers/msgpack.go | 7 ++++ test_helpers/request_mock.go | 3 +- uuid/msgpack.go | 16 ++++++++ uuid/msgpack_helper_test.go | 7 ++++ uuid/uuid.go | 10 +---- uuid/uuid_test.go | 3 +- 33 files changed, 296 insertions(+), 220 deletions(-) create mode 100644 datetime/msgpack.go create mode 100644 datetime/msgpack_helper_test.go create mode 100644 decimal/msgpack.go create mode 100644 decimal/msgpack_helper_test.go create mode 100644 msgpack.go create mode 100644 msgpack_helper_test.go create mode 100644 queue/msgpack.go create mode 100644 queue/msgpack_helper_test.go create mode 100644 test_helpers/msgpack.go create mode 100644 uuid/msgpack.go create mode 100644 uuid/msgpack_helper_test.go diff --git a/client_tools.go b/client_tools.go index 20bfa722f..88da9c577 100644 --- a/client_tools.go +++ b/client_tools.go @@ -1,16 +1,12 @@ package tarantool -import ( - "gopkg.in/vmihailenco/msgpack.v2" -) - // IntKey is utility type for passing integer key to Select*, Update* and Delete*. // It serializes to array with single integer element. type IntKey struct { I int } -func (k IntKey) EncodeMsgpack(enc *msgpack.Encoder) error { +func (k IntKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(1) enc.EncodeInt(k.I) return nil @@ -22,7 +18,7 @@ type UintKey struct { I uint } -func (k UintKey) EncodeMsgpack(enc *msgpack.Encoder) error { +func (k UintKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(1) enc.EncodeUint(k.I) return nil @@ -34,7 +30,7 @@ type StringKey struct { S string } -func (k StringKey) EncodeMsgpack(enc *msgpack.Encoder) error { +func (k StringKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(1) enc.EncodeString(k.S) return nil @@ -46,7 +42,7 @@ type IntIntKey struct { I1, I2 int } -func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error { +func (k IntIntKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(2) enc.EncodeInt(k.I1) enc.EncodeInt(k.I2) @@ -60,7 +56,7 @@ type Op struct { Arg interface{} } -func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error { +func (o Op) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(3) enc.EncodeString(o.Op) enc.EncodeInt(o.Field) @@ -148,7 +144,7 @@ type OpSplice struct { Replace string } -func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error { +func (o OpSplice) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(5) enc.EncodeString(o.Op) enc.EncodeInt(o.Field) diff --git a/connection.go b/connection.go index a1e485ba8..957b51506 100644 --- a/connection.go +++ b/connection.go @@ -15,8 +15,6 @@ import ( "sync" "sync/atomic" "time" - - "gopkg.in/vmihailenco/msgpack.v2" ) const requestsMap = 128 @@ -142,7 +140,7 @@ type Connection struct { rlimit chan struct{} opts Opts state uint32 - dec *msgpack.Decoder + dec *decoder lenbuf [PacketLengthBytes]byte lastStreamId uint64 @@ -199,7 +197,7 @@ type connShard struct { requestsWithCtx [requestsMap]futureList bufmut sync.Mutex buf smallWBuf - enc *msgpack.Encoder + enc *encoder } // Greeting is a message sent by Tarantool on connect. @@ -320,7 +318,7 @@ func Connect(addr string, opts Opts) (conn *Connection, err error) { Greeting: &Greeting{}, control: make(chan struct{}), opts: opts, - dec: msgpack.NewDecoder(&smallBuf{}), + dec: newDecoder(&smallBuf{}), } maxprocs := uint32(runtime.GOMAXPROCS(-1)) if conn.opts.Concurrency == 0 || conn.opts.Concurrency > maxprocs*128 { @@ -531,7 +529,7 @@ func (conn *Connection) dial() (err error) { return } -func pack(h *smallWBuf, enc *msgpack.Encoder, reqid uint32, +func pack(h *smallWBuf, enc *encoder, reqid uint32, req Request, streamId uint64, res SchemaResolver) (err error) { hl := h.Len() @@ -569,7 +567,7 @@ func pack(h *smallWBuf, enc *msgpack.Encoder, reqid uint32, func (conn *Connection) writeAuthRequest(w *bufio.Writer, scramble []byte) (err error) { var packet smallWBuf req := newAuthRequest(conn.opts.User, string(scramble)) - err = pack(&packet, msgpack.NewEncoder(&packet), 0, req, ignoreStreamId, conn.Schema) + err = pack(&packet, newEncoder(&packet), 0, req, ignoreStreamId, conn.Schema) if err != nil { return errors.New("auth: pack error " + err.Error()) @@ -916,7 +914,7 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) { firstWritten := shard.buf.Len() == 0 if shard.buf.Cap() == 0 { shard.buf.b = make([]byte, 0, 128) - shard.enc = msgpack.NewEncoder(&shard.buf) + shard.enc = newEncoder(&shard.buf) } blen := shard.buf.Len() reqid := fut.requestId diff --git a/datetime/datetime.go b/datetime/datetime.go index e9664c7ae..e8f1bdea7 100644 --- a/datetime/datetime.go +++ b/datetime/datetime.go @@ -13,8 +13,6 @@ import ( "encoding/binary" "fmt" "time" - - "gopkg.in/vmihailenco/msgpack.v2" ) // Datetime MessagePack serialization schema is an MP_EXT extension, which @@ -101,9 +99,6 @@ func (dtime *Datetime) ToTime() time.Time { return dtime.time } -var _ msgpack.Marshaler = (*Datetime)(nil) -var _ msgpack.Unmarshaler = (*Datetime)(nil) - func (dtime *Datetime) MarshalMsgpack() ([]byte, error) { tm := dtime.ToTime() @@ -152,7 +147,3 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error { } return err } - -func init() { - msgpack.RegisterExt(datetime_extId, &Datetime{}) -} diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 2de8888d7..035b1025b 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -12,7 +12,6 @@ import ( . "github.com/tarantool/go-tarantool" . "github.com/tarantool/go-tarantool/datetime" "github.com/tarantool/go-tarantool/test_helpers" - "gopkg.in/vmihailenco/msgpack.v2" ) var lesserBoundaryTimes = []time.Time{ @@ -270,7 +269,7 @@ type Tuple1 struct { Datetime Datetime } -func (t *Tuple1) EncodeMsgpack(e *msgpack.Encoder) error { +func (t *Tuple1) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(2); err != nil { return err } @@ -280,7 +279,7 @@ func (t *Tuple1) EncodeMsgpack(e *msgpack.Encoder) error { return nil } -func (t *Tuple1) DecodeMsgpack(d *msgpack.Decoder) error { +func (t *Tuple1) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { @@ -296,7 +295,7 @@ func (t *Tuple1) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (ev *Event) EncodeMsgpack(e *msgpack.Encoder) error { +func (ev *Event) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(2); err != nil { return err } @@ -309,7 +308,7 @@ func (ev *Event) EncodeMsgpack(e *msgpack.Encoder) error { return nil } -func (ev *Event) DecodeMsgpack(d *msgpack.Decoder) error { +func (ev *Event) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { @@ -329,7 +328,7 @@ func (ev *Event) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { +func (c *Tuple2) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(3); err != nil { return err } @@ -343,7 +342,7 @@ func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { return nil } -func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { +func (c *Tuple2) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { @@ -525,7 +524,7 @@ func TestMPEncode(t *testing.T) { if err != nil { t.Fatalf("Unable to create Datetime from %s: %s", tm, err) } - buf, err := msgpack.Marshal(dt) + buf, err := marshal(dt) if err != nil { t.Fatalf("Marshalling failed: %s", err.Error()) } @@ -549,7 +548,7 @@ func TestMPDecode(t *testing.T) { } buf, _ := hex.DecodeString(testcase.mpBuf) var v Datetime - err = msgpack.Unmarshal(buf, &v) + err = unmarshal(buf, &v) if err != nil { t.Fatalf("Unmarshalling failed: %s", err.Error()) } diff --git a/datetime/msgpack.go b/datetime/msgpack.go new file mode 100644 index 000000000..915c541ec --- /dev/null +++ b/datetime/msgpack.go @@ -0,0 +1,9 @@ +package datetime + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +func init() { + msgpack.RegisterExt(datetime_extId, &Datetime{}) +} diff --git a/datetime/msgpack_helper_test.go b/datetime/msgpack_helper_test.go new file mode 100644 index 000000000..ba283db18 --- /dev/null +++ b/datetime/msgpack_helper_test.go @@ -0,0 +1,16 @@ +package datetime_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func marshal(v interface{}) ([]byte, error) { + return msgpack.Marshal(v) +} + +func unmarshal(data []byte, v interface{}) error { + return msgpack.Unmarshal(data, v) +} diff --git a/decimal/decimal.go b/decimal/decimal.go index 66587feec..b92391ac2 100644 --- a/decimal/decimal.go +++ b/decimal/decimal.go @@ -19,7 +19,6 @@ import ( "fmt" "github.com/shopspring/decimal" - "gopkg.in/vmihailenco/msgpack.v2" ) // Decimal numbers have 38 digits of precision, that is, the total @@ -56,9 +55,6 @@ func NewDecimalFromString(src string) (result *Decimal, err error) { return } -var _ msgpack.Marshaler = (*Decimal)(nil) -var _ msgpack.Unmarshaler = (*Decimal)(nil) - func (decNum *Decimal) MarshalMsgpack() ([]byte, error) { one := decimal.NewFromInt(1) maxSupportedDecimal := decimal.New(1, DecimalPrecision).Sub(one) // 10^DecimalPrecision - 1 @@ -99,7 +95,3 @@ func (decNum *Decimal) UnmarshalMsgpack(b []byte) error { return nil } - -func init() { - msgpack.RegisterExt(decimalExtID, &Decimal{}) -} diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go index 499d939c2..9eb056ca8 100644 --- a/decimal/decimal_test.go +++ b/decimal/decimal_test.go @@ -13,7 +13,6 @@ import ( . "github.com/tarantool/go-tarantool" . "github.com/tarantool/go-tarantool/decimal" "github.com/tarantool/go-tarantool/test_helpers" - "gopkg.in/vmihailenco/msgpack.v2" ) var isDecimalSupported = false @@ -40,14 +39,14 @@ type TupleDecimal struct { number Decimal } -func (t *TupleDecimal) EncodeMsgpack(e *msgpack.Encoder) error { +func (t *TupleDecimal) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(1); err != nil { return err } return e.EncodeValue(reflect.ValueOf(&t.number)) } -func (t *TupleDecimal) DecodeMsgpack(d *msgpack.Decoder) error { +func (t *TupleDecimal) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { @@ -144,11 +143,11 @@ func TestMPEncodeDecode(t *testing.T) { } var buf []byte tuple := TupleDecimal{number: *decNum} - if buf, err = msgpack.Marshal(&tuple); err != nil { + if buf, err = marshal(&tuple); err != nil { t.Fatalf("Failed to encode decimal number '%s' to a MessagePack buffer: %s", testcase.numString, err) } var v TupleDecimal - if err = msgpack.Unmarshal(buf, &v); err != nil { + if err = unmarshal(buf, &v); err != nil { t.Fatalf("Failed to decode MessagePack buffer '%x' to a decimal number: %s", buf, err) } if !decNum.Equal(v.number.Decimal) { @@ -247,7 +246,7 @@ func TestEncodeMaxNumber(t *testing.T) { referenceErrMsg := "msgpack: decimal number is bigger than maximum supported number (10^38 - 1)" decNum := decimal.New(1, DecimalPrecision) // // 10^DecimalPrecision tuple := TupleDecimal{number: *NewDecimal(decNum)} - _, err := msgpack.Marshal(&tuple) + _, err := marshal(&tuple) if err == nil { t.Fatalf("It is possible to encode a number unsupported by Tarantool") } @@ -261,7 +260,7 @@ func TestEncodeMinNumber(t *testing.T) { two := decimal.NewFromInt(2) decNum := decimal.New(1, DecimalPrecision).Neg().Sub(two) // -10^DecimalPrecision - 2 tuple := TupleDecimal{number: *NewDecimal(decNum)} - _, err := msgpack.Marshal(&tuple) + _, err := marshal(&tuple) if err == nil { t.Fatalf("It is possible to encode a number unsupported by Tarantool") } @@ -280,10 +279,10 @@ func benchmarkMPEncodeDecode(b *testing.B, src decimal.Decimal, dst interface{}) var err error for i := 0; i < b.N; i++ { tuple := TupleDecimal{number: *NewDecimal(src)} - if buf, err = msgpack.Marshal(&tuple); err != nil { + if buf, err = marshal(&tuple); err != nil { b.Fatal(err) } - if err = msgpack.Unmarshal(buf, &v); err != nil { + if err = unmarshal(buf, &v); err != nil { b.Fatal(err) } } @@ -310,7 +309,7 @@ func BenchmarkMPEncodeDecimal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - msgpack.Marshal(decNum) + marshal(decNum) } }) } @@ -324,13 +323,13 @@ func BenchmarkMPDecodeDecimal(b *testing.B) { b.Fatal(err) } var buf []byte - if buf, err = msgpack.Marshal(decNum); err != nil { + if buf, err = marshal(decNum); err != nil { b.Fatal(err) } b.ResetTimer() var v TupleDecimal for i := 0; i < b.N; i++ { - msgpack.Unmarshal(buf, &v) + unmarshal(buf, &v) } }) @@ -417,7 +416,7 @@ func TestMPEncode(t *testing.T) { if err != nil { t.Fatalf("NewDecimalFromString() failed: %s", err.Error()) } - buf, err := msgpack.Marshal(dec) + buf, err := marshal(dec) if err != nil { t.Fatalf("Marshalling failed: %s", err.Error()) } @@ -442,7 +441,7 @@ func TestMPDecode(t *testing.T) { t.Fatalf("hex.DecodeString() failed: %s", err) } var v interface{} - err = msgpack.Unmarshal(mpBuf, &v) + err = unmarshal(mpBuf, &v) if err != nil { t.Fatalf("Unmarshalling failed: %s", err.Error()) } diff --git a/decimal/msgpack.go b/decimal/msgpack.go new file mode 100644 index 000000000..ca360172f --- /dev/null +++ b/decimal/msgpack.go @@ -0,0 +1,9 @@ +package decimal + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +func init() { + msgpack.RegisterExt(decimalExtID, &Decimal{}) +} diff --git a/decimal/msgpack_helper_test.go b/decimal/msgpack_helper_test.go new file mode 100644 index 000000000..3013f4cf0 --- /dev/null +++ b/decimal/msgpack_helper_test.go @@ -0,0 +1,16 @@ +package decimal_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func marshal(v interface{}) ([]byte, error) { + return msgpack.Marshal(v) +} + +func unmarshal(data []byte, v interface{}) error { + return msgpack.Unmarshal(data, v) +} diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index d524dfde8..cff50da50 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/tarantool/go-tarantool" - "gopkg.in/vmihailenco/msgpack.v2" ) type Tuple2 struct { @@ -24,7 +23,7 @@ type Tuple3 struct { Members []Member } -func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { +func (c *Tuple2) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(3); err != nil { return err } @@ -38,7 +37,7 @@ func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { return nil } -func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { +func (c *Tuple2) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { diff --git a/export_test.go b/export_test.go index 0492c1a5b..cc9a2a594 100644 --- a/export_test.go +++ b/export_test.go @@ -1,10 +1,9 @@ package tarantool import ( + "io" "net" "time" - - "gopkg.in/vmihailenco/msgpack.v2" ) func SslDialTimeout(network, address string, timeout time.Duration, @@ -18,96 +17,100 @@ func SslCreateContext(opts SslOpts) (ctx interface{}, err error) { // RefImplPingBody is reference implementation for filling of a ping // request's body. -func RefImplPingBody(enc *msgpack.Encoder) error { +func RefImplPingBody(enc *encoder) error { return fillPing(enc) } // RefImplSelectBody is reference implementation for filling of a select // request's body. -func RefImplSelectBody(enc *msgpack.Encoder, space, index, offset, limit, iterator uint32, key interface{}) error { +func RefImplSelectBody(enc *encoder, space, index, offset, limit, iterator uint32, key interface{}) error { return fillSelect(enc, space, index, offset, limit, iterator, key) } // RefImplInsertBody is reference implementation for filling of an insert // request's body. -func RefImplInsertBody(enc *msgpack.Encoder, space uint32, tuple interface{}) error { +func RefImplInsertBody(enc *encoder, space uint32, tuple interface{}) error { return fillInsert(enc, space, tuple) } // RefImplReplaceBody is reference implementation for filling of a replace // request's body. -func RefImplReplaceBody(enc *msgpack.Encoder, space uint32, tuple interface{}) error { +func RefImplReplaceBody(enc *encoder, space uint32, tuple interface{}) error { return fillInsert(enc, space, tuple) } // RefImplDeleteBody is reference implementation for filling of a delete // request's body. -func RefImplDeleteBody(enc *msgpack.Encoder, space, index uint32, key interface{}) error { +func RefImplDeleteBody(enc *encoder, space, index uint32, key interface{}) error { return fillDelete(enc, space, index, key) } // RefImplUpdateBody is reference implementation for filling of an update // request's body. -func RefImplUpdateBody(enc *msgpack.Encoder, space, index uint32, key, ops interface{}) error { +func RefImplUpdateBody(enc *encoder, space, index uint32, key, ops interface{}) error { return fillUpdate(enc, space, index, key, ops) } // RefImplUpsertBody is reference implementation for filling of an upsert // request's body. -func RefImplUpsertBody(enc *msgpack.Encoder, space uint32, tuple, ops interface{}) error { +func RefImplUpsertBody(enc *encoder, space uint32, tuple, ops interface{}) error { return fillUpsert(enc, space, tuple, ops) } // RefImplCallBody is reference implementation for filling of a call or call17 // request's body. -func RefImplCallBody(enc *msgpack.Encoder, function string, args interface{}) error { +func RefImplCallBody(enc *encoder, function string, args interface{}) error { return fillCall(enc, function, args) } // RefImplEvalBody is reference implementation for filling of an eval // request's body. -func RefImplEvalBody(enc *msgpack.Encoder, expr string, args interface{}) error { +func RefImplEvalBody(enc *encoder, expr string, args interface{}) error { return fillEval(enc, expr, args) } // RefImplExecuteBody is reference implementation for filling of an execute // request's body. -func RefImplExecuteBody(enc *msgpack.Encoder, expr string, args interface{}) error { +func RefImplExecuteBody(enc *encoder, expr string, args interface{}) error { return fillExecute(enc, expr, args) } // RefImplPrepareBody is reference implementation for filling of an prepare // request's body. -func RefImplPrepareBody(enc *msgpack.Encoder, expr string) error { +func RefImplPrepareBody(enc *encoder, expr string) error { return fillPrepare(enc, expr) } // RefImplUnprepareBody is reference implementation for filling of an execute prepared // request's body. -func RefImplExecutePreparedBody(enc *msgpack.Encoder, stmt Prepared, args interface{}) error { +func RefImplExecutePreparedBody(enc *encoder, stmt Prepared, args interface{}) error { return fillExecutePrepared(enc, stmt, args) } // RefImplUnprepareBody is reference implementation for filling of an unprepare // request's body. -func RefImplUnprepareBody(enc *msgpack.Encoder, stmt Prepared) error { +func RefImplUnprepareBody(enc *encoder, stmt Prepared) error { return fillUnprepare(enc, stmt) } // RefImplBeginBody is reference implementation for filling of an begin // request's body. -func RefImplBeginBody(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout time.Duration) error { +func RefImplBeginBody(enc *encoder, txnIsolation TxnIsolationLevel, timeout time.Duration) error { return fillBegin(enc, txnIsolation, timeout) } // RefImplCommitBody is reference implementation for filling of an commit // request's body. -func RefImplCommitBody(enc *msgpack.Encoder) error { +func RefImplCommitBody(enc *encoder) error { return fillCommit(enc) } // RefImplRollbackBody is reference implementation for filling of an rollback // request's body. -func RefImplRollbackBody(enc *msgpack.Encoder) error { +func RefImplRollbackBody(enc *encoder) error { return fillRollback(enc) } + +func NewEncoder(w io.Writer) *encoder { + return newEncoder(w) +} diff --git a/msgpack.go b/msgpack.go new file mode 100644 index 000000000..bcdcc06ce --- /dev/null +++ b/msgpack.go @@ -0,0 +1,39 @@ +package tarantool + +import ( + "io" + + "gopkg.in/vmihailenco/msgpack.v2" + msgpcode "gopkg.in/vmihailenco/msgpack.v2/codes" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func newEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} + +func newDecoder(r io.Reader) *decoder { + return msgpack.NewDecoder(r) +} + +func msgpackIsUint(code byte) bool { + return code == msgpcode.Uint8 || code == msgpcode.Uint16 || + code == msgpcode.Uint32 || code == msgpcode.Uint64 || + msgpcode.IsFixedNum(code) +} + +func msgpackIsMap(code byte) bool { + return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code) +} + +func msgpackIsArray(code byte) bool { + return code == msgpcode.Array16 || code == msgpcode.Array32 || + msgpcode.IsFixedArray(code) +} + +func msgpackIsString(code byte) bool { + return msgpcode.IsFixedString(code) || code == msgpcode.Str8 || + code == msgpcode.Str16 || code == msgpcode.Str32 +} diff --git a/msgpack_helper_test.go b/msgpack_helper_test.go new file mode 100644 index 000000000..8623059e5 --- /dev/null +++ b/msgpack_helper_test.go @@ -0,0 +1,8 @@ +package tarantool_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder diff --git a/prepared.go b/prepared.go index 2546d34b6..91c5238a7 100644 --- a/prepared.go +++ b/prepared.go @@ -3,8 +3,6 @@ package tarantool import ( "context" "fmt" - - "gopkg.in/vmihailenco/msgpack.v2" ) // PreparedID is a type for Prepared Statement ID @@ -20,19 +18,19 @@ type Prepared struct { Conn *Connection } -func fillPrepare(enc *msgpack.Encoder, expr string) error { +func fillPrepare(enc *encoder, expr string) error { enc.EncodeMapLen(1) enc.EncodeUint(KeySQLText) return enc.EncodeString(expr) } -func fillUnprepare(enc *msgpack.Encoder, stmt Prepared) error { +func fillUnprepare(enc *encoder, stmt Prepared) error { enc.EncodeMapLen(1) enc.EncodeUint(KeyStmtID) return enc.EncodeUint(uint(stmt.StatementID)) } -func fillExecutePrepared(enc *msgpack.Encoder, stmt Prepared, args interface{}) error { +func fillExecutePrepared(enc *encoder, stmt Prepared, args interface{}) error { enc.EncodeMapLen(2) enc.EncodeUint(KeyStmtID) enc.EncodeUint(uint(stmt.StatementID)) @@ -75,7 +73,7 @@ func NewPrepareRequest(expr string) *PrepareRequest { } // Body fills an encoder with the execute request body. -func (req *PrepareRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *PrepareRequest) Body(res SchemaResolver, enc *encoder) error { return fillPrepare(enc, req.expr) } @@ -111,7 +109,7 @@ func (req *UnprepareRequest) Conn() *Connection { } // Body fills an encoder with the execute request body. -func (req *UnprepareRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *UnprepareRequest) Body(res SchemaResolver, enc *encoder) error { return fillUnprepare(enc, *req.stmt) } @@ -156,7 +154,7 @@ func (req *ExecutePreparedRequest) Args(args interface{}) *ExecutePreparedReques } // Body fills an encoder with the execute request body. -func (req *ExecutePreparedRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *ExecutePreparedRequest) Body(res SchemaResolver, enc *encoder) error { return fillExecutePrepared(enc, *req.stmt, req.args) } diff --git a/queue/example_msgpack_test.go b/queue/example_msgpack_test.go index 89bdad5a3..2ed7f5542 100644 --- a/queue/example_msgpack_test.go +++ b/queue/example_msgpack_test.go @@ -15,14 +15,13 @@ import ( "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/queue" - "gopkg.in/vmihailenco/msgpack.v2" ) type dummyData struct { Dummy bool } -func (c *dummyData) DecodeMsgpack(d *msgpack.Decoder) error { +func (c *dummyData) DecodeMsgpack(d *decoder) error { var err error if c.Dummy, err = d.DecodeBool(); err != nil { return err @@ -30,7 +29,7 @@ func (c *dummyData) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (c *dummyData) EncodeMsgpack(e *msgpack.Encoder) error { +func (c *dummyData) EncodeMsgpack(e *encoder) error { return e.EncodeBool(c.Dummy) } diff --git a/queue/msgpack.go b/queue/msgpack.go new file mode 100644 index 000000000..d614374ce --- /dev/null +++ b/queue/msgpack.go @@ -0,0 +1,7 @@ +package queue + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type decoder = msgpack.Decoder diff --git a/queue/msgpack_helper_test.go b/queue/msgpack_helper_test.go new file mode 100644 index 000000000..cfb8d8e89 --- /dev/null +++ b/queue/msgpack_helper_test.go @@ -0,0 +1,8 @@ +package queue_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder diff --git a/queue/queue.go b/queue/queue.go index 68a98672b..4fe8fe296 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -13,7 +13,6 @@ import ( "time" "github.com/tarantool/go-tarantool" - msgpack "gopkg.in/vmihailenco/msgpack.v2" ) // Queue is a handle to Tarantool queue's tube. @@ -336,7 +335,7 @@ type queueData struct { result interface{} } -func (qd *queueData) DecodeMsgpack(d *msgpack.Decoder) error { +func (qd *queueData) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { diff --git a/queue/queue_test.go b/queue/queue_test.go index 25af17b12..807ac79e0 100644 --- a/queue/queue_test.go +++ b/queue/queue_test.go @@ -11,7 +11,6 @@ import ( . "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/queue" "github.com/tarantool/go-tarantool/test_helpers" - "gopkg.in/vmihailenco/msgpack.v2" ) var server = "127.0.0.1:3013" @@ -183,7 +182,7 @@ type customData struct { customField string } -func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { +func (c *customData) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { @@ -198,7 +197,7 @@ func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (c *customData) EncodeMsgpack(e *msgpack.Encoder) error { +func (c *customData) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(1); err != nil { return err } diff --git a/queue/task.go b/queue/task.go index add05efd6..2f4242311 100644 --- a/queue/task.go +++ b/queue/task.go @@ -2,8 +2,6 @@ package queue import ( "fmt" - - msgpack "gopkg.in/vmihailenco/msgpack.v2" ) // Task represents a task from Tarantool queue's tube. @@ -14,7 +12,7 @@ type Task struct { q *queue } -func (t *Task) DecodeMsgpack(d *msgpack.Decoder) error { +func (t *Task) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { diff --git a/request.go b/request.go index 7d14d3710..625380306 100644 --- a/request.go +++ b/request.go @@ -6,11 +6,9 @@ import ( "reflect" "strings" "sync" - - "gopkg.in/vmihailenco/msgpack.v2" ) -func fillSearch(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) error { +func fillSearch(enc *encoder, spaceNo, indexNo uint32, key interface{}) error { enc.EncodeUint(KeySpaceNo) enc.EncodeUint(uint(spaceNo)) enc.EncodeUint(KeyIndexNo) @@ -19,7 +17,7 @@ func fillSearch(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) return enc.Encode(key) } -func fillIterator(enc *msgpack.Encoder, offset, limit, iterator uint32) { +func fillIterator(enc *encoder, offset, limit, iterator uint32) { enc.EncodeUint(KeyIterator) enc.EncodeUint(uint(iterator)) enc.EncodeUint(KeyOffset) @@ -28,7 +26,7 @@ func fillIterator(enc *msgpack.Encoder, offset, limit, iterator uint32) { enc.EncodeUint(uint(limit)) } -func fillInsert(enc *msgpack.Encoder, spaceNo uint32, tuple interface{}) error { +func fillInsert(enc *encoder, spaceNo uint32, tuple interface{}) error { enc.EncodeMapLen(2) enc.EncodeUint(KeySpaceNo) enc.EncodeUint(uint(spaceNo)) @@ -36,13 +34,13 @@ func fillInsert(enc *msgpack.Encoder, spaceNo uint32, tuple interface{}) error { return enc.Encode(tuple) } -func fillSelect(enc *msgpack.Encoder, spaceNo, indexNo, offset, limit, iterator uint32, key interface{}) error { +func fillSelect(enc *encoder, spaceNo, indexNo, offset, limit, iterator uint32, key interface{}) error { enc.EncodeMapLen(6) fillIterator(enc, offset, limit, iterator) return fillSearch(enc, spaceNo, indexNo, key) } -func fillUpdate(enc *msgpack.Encoder, spaceNo, indexNo uint32, key, ops interface{}) error { +func fillUpdate(enc *encoder, spaceNo, indexNo uint32, key, ops interface{}) error { enc.EncodeMapLen(4) if err := fillSearch(enc, spaceNo, indexNo, key); err != nil { return err @@ -51,7 +49,7 @@ func fillUpdate(enc *msgpack.Encoder, spaceNo, indexNo uint32, key, ops interfac return enc.Encode(ops) } -func fillUpsert(enc *msgpack.Encoder, spaceNo uint32, tuple, ops interface{}) error { +func fillUpsert(enc *encoder, spaceNo uint32, tuple, ops interface{}) error { enc.EncodeMapLen(3) enc.EncodeUint(KeySpaceNo) enc.EncodeUint(uint(spaceNo)) @@ -63,12 +61,12 @@ func fillUpsert(enc *msgpack.Encoder, spaceNo uint32, tuple, ops interface{}) er return enc.Encode(ops) } -func fillDelete(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) error { +func fillDelete(enc *encoder, spaceNo, indexNo uint32, key interface{}) error { enc.EncodeMapLen(3) return fillSearch(enc, spaceNo, indexNo, key) } -func fillCall(enc *msgpack.Encoder, functionName string, args interface{}) error { +func fillCall(enc *encoder, functionName string, args interface{}) error { enc.EncodeMapLen(2) enc.EncodeUint(KeyFunctionName) enc.EncodeString(functionName) @@ -76,7 +74,7 @@ func fillCall(enc *msgpack.Encoder, functionName string, args interface{}) error return enc.Encode(args) } -func fillEval(enc *msgpack.Encoder, expr string, args interface{}) error { +func fillEval(enc *encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) enc.EncodeUint(KeyExpression) enc.EncodeString(expr) @@ -84,7 +82,7 @@ func fillEval(enc *msgpack.Encoder, expr string, args interface{}) error { return enc.Encode(args) } -func fillExecute(enc *msgpack.Encoder, expr string, args interface{}) error { +func fillExecute(enc *encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) enc.EncodeUint(KeySQLText) enc.EncodeString(expr) @@ -92,7 +90,7 @@ func fillExecute(enc *msgpack.Encoder, expr string, args interface{}) error { return encodeSQLBind(enc, args) } -func fillPing(enc *msgpack.Encoder) error { +func fillPing(enc *encoder) error { return enc.EncodeMapLen(0) } @@ -197,7 +195,7 @@ type single struct { found bool } -func (s *single) DecodeMsgpack(d *msgpack.Decoder) error { +func (s *single) DecodeMsgpack(d *decoder) error { var err error var len int if len, err = d.DecodeArrayLen(); err != nil { @@ -404,7 +402,7 @@ type KeyValueBind struct { // to avoid extra allocations in heap by calling strings.ToLower() var lowerCaseNames sync.Map -func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error { +func encodeSQLBind(enc *encoder, from interface{}) error { // internal function for encoding single map in msgpack encodeKeyInterface := func(key string, val interface{}) error { if err := enc.EncodeMapLen(1); err != nil { @@ -537,7 +535,7 @@ type Request interface { // Code returns a IPROTO code for the request. Code() int32 // Body fills an encoder with a request body. - Body(resolver SchemaResolver, enc *msgpack.Encoder) error + Body(resolver SchemaResolver, enc *encoder) error // Ctx returns a context of the request. Ctx() context.Context } @@ -597,7 +595,7 @@ func newAuthRequest(user, scramble string) *authRequest { } // Body fills an encoder with the auth request body. -func (req *authRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *authRequest) Body(res SchemaResolver, enc *encoder) error { return enc.Encode(map[uint32]interface{}{ KeyUserName: req.user, KeyTuple: []interface{}{string("chap-sha1"), string(req.scramble)}, @@ -618,7 +616,7 @@ func NewPingRequest() *PingRequest { } // Body fills an encoder with the ping request body. -func (req *PingRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *PingRequest) Body(res SchemaResolver, enc *encoder) error { return fillPing(enc) } @@ -694,7 +692,7 @@ func (req *SelectRequest) Key(key interface{}) *SelectRequest { } // Body fills an encoder with the select request body. -func (req *SelectRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *SelectRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) if err != nil { return err @@ -738,7 +736,7 @@ func (req *InsertRequest) Tuple(tuple interface{}) *InsertRequest { } // Body fills an encoder with the insert request body. -func (req *InsertRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *InsertRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) if err != nil { return err @@ -782,7 +780,7 @@ func (req *ReplaceRequest) Tuple(tuple interface{}) *ReplaceRequest { } // Body fills an encoder with the replace request body. -func (req *ReplaceRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *ReplaceRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) if err != nil { return err @@ -833,7 +831,7 @@ func (req *DeleteRequest) Key(key interface{}) *DeleteRequest { } // Body fills an encoder with the delete request body. -func (req *DeleteRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *DeleteRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) if err != nil { return err @@ -895,7 +893,7 @@ func (req *UpdateRequest) Operations(ops *Operations) *UpdateRequest { } // Body fills an encoder with the update request body. -func (req *UpdateRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *UpdateRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) if err != nil { return err @@ -950,7 +948,7 @@ func (req *UpsertRequest) Operations(ops *Operations) *UpsertRequest { } // Body fills an encoder with the upsert request body. -func (req *UpsertRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *UpsertRequest) Body(res SchemaResolver, enc *encoder) error { spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) if err != nil { return err @@ -997,7 +995,7 @@ func (req *CallRequest) Args(args interface{}) *CallRequest { } // Body fills an encoder with the call request body. -func (req *CallRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *CallRequest) Body(res SchemaResolver, enc *encoder) error { return fillCall(enc, req.function, req.args) } @@ -1054,7 +1052,7 @@ func (req *EvalRequest) Args(args interface{}) *EvalRequest { } // Body fills an encoder with the eval request body. -func (req *EvalRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *EvalRequest) Body(res SchemaResolver, enc *encoder) error { return fillEval(enc, req.expr, req.args) } @@ -1094,7 +1092,7 @@ func (req *ExecuteRequest) Args(args interface{}) *ExecuteRequest { } // Body fills an encoder with the execute request body. -func (req *ExecuteRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *ExecuteRequest) Body(res SchemaResolver, enc *encoder) error { return fillExecute(enc, req.expr, req.args) } diff --git a/request_test.go b/request_test.go index b1a558b59..6ba5cecd4 100644 --- a/request_test.go +++ b/request_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/assert" . "github.com/tarantool/go-tarantool" - "gopkg.in/vmihailenco/msgpack.v2" ) const invalidSpaceMsg = "invalid space" @@ -61,7 +60,7 @@ func assertBodyCall(t testing.TB, requests []Request, errorMsg string) { const errBegin = "An unexpected Request.Body() " for _, req := range requests { var reqBuf bytes.Buffer - enc := msgpack.NewEncoder(&reqBuf) + enc := NewEncoder(&reqBuf) err := req.Body(&resolver, enc) if err != nil && errorMsg != "" && err.Error() != errorMsg { @@ -80,7 +79,7 @@ func assertBodyEqual(t testing.TB, reference []byte, req Request) { t.Helper() var reqBuf bytes.Buffer - reqEnc := msgpack.NewEncoder(&reqBuf) + reqEnc := NewEncoder(&reqBuf) err := req.Body(&resolver, reqEnc) if err != nil { @@ -196,7 +195,7 @@ func TestRequestsCodes(t *testing.T) { func TestPingRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplPingBody(refEnc) if err != nil { t.Errorf("An unexpected RefImplPingBody() error: %q", err.Error()) @@ -210,7 +209,7 @@ func TestPingRequestDefaultValues(t *testing.T) { func TestSelectRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterAll, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -225,7 +224,7 @@ func TestSelectRequestDefaultIteratorEqIfKey(t *testing.T) { var refBuf bytes.Buffer key := []interface{}{uint(18)} - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterEq, key) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -242,7 +241,7 @@ func TestSelectRequestIteratorNotChangedIfKey(t *testing.T) { key := []interface{}{uint(678)} const iter = IterGe - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, iter, key) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -262,7 +261,7 @@ func TestSelectRequestSetters(t *testing.T) { key := []interface{}{uint(36)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplSelectBody(refEnc, validSpace, validIndex, offset, limit, iter, key) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -281,7 +280,7 @@ func TestSelectRequestSetters(t *testing.T) { func TestInsertRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplInsertBody(refEnc, validSpace, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) @@ -296,7 +295,7 @@ func TestInsertRequestSetters(t *testing.T) { tuple := []interface{}{uint(24)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplInsertBody(refEnc, validSpace, tuple) if err != nil { t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) @@ -311,7 +310,7 @@ func TestInsertRequestSetters(t *testing.T) { func TestReplaceRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplReplaceBody(refEnc, validSpace, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) @@ -326,7 +325,7 @@ func TestReplaceRequestSetters(t *testing.T) { tuple := []interface{}{uint(99)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplReplaceBody(refEnc, validSpace, tuple) if err != nil { t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) @@ -341,7 +340,7 @@ func TestReplaceRequestSetters(t *testing.T) { func TestDeleteRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplDeleteBody(refEnc, validSpace, defaultIndex, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) @@ -356,7 +355,7 @@ func TestDeleteRequestSetters(t *testing.T) { key := []interface{}{uint(923)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplDeleteBody(refEnc, validSpace, validIndex, key) if err != nil { t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) @@ -372,7 +371,7 @@ func TestDeleteRequestSetters(t *testing.T) { func TestUpdateRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplUpdateBody(refEnc, validSpace, defaultIndex, []interface{}{}, []Op{}) if err != nil { t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) @@ -388,7 +387,7 @@ func TestUpdateRequestSetters(t *testing.T) { refOps, reqOps := getTestOps() var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplUpdateBody(refEnc, validSpace, validIndex, key, refOps) if err != nil { t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) @@ -405,7 +404,7 @@ func TestUpdateRequestSetters(t *testing.T) { func TestUpsertRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplUpsertBody(refEnc, validSpace, []interface{}{}, []Op{}) if err != nil { t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) @@ -421,7 +420,7 @@ func TestUpsertRequestSetters(t *testing.T) { refOps, reqOps := getTestOps() var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplUpsertBody(refEnc, validSpace, tuple, refOps) if err != nil { t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) @@ -437,7 +436,7 @@ func TestUpsertRequestSetters(t *testing.T) { func TestCallRequestsDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplCallBody(refEnc, validExpr, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) @@ -456,7 +455,7 @@ func TestCallRequestsSetters(t *testing.T) { args := []interface{}{uint(34)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplCallBody(refEnc, validExpr, args) if err != nil { t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) @@ -477,7 +476,7 @@ func TestCallRequestsSetters(t *testing.T) { func TestEvalRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplEvalBody(refEnc, validExpr, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplEvalBody() error: %q", err.Error()) @@ -492,7 +491,7 @@ func TestEvalRequestSetters(t *testing.T) { args := []interface{}{uint(34), int(12)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplEvalBody(refEnc, validExpr, args) if err != nil { t.Errorf("An unexpected RefImplEvalBody() error: %q", err.Error()) @@ -507,7 +506,7 @@ func TestEvalRequestSetters(t *testing.T) { func TestExecuteRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplExecuteBody(refEnc, validExpr, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplExecuteBody() error: %q", err.Error()) @@ -522,7 +521,7 @@ func TestExecuteRequestSetters(t *testing.T) { args := []interface{}{uint(11)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplExecuteBody(refEnc, validExpr, args) if err != nil { t.Errorf("An unexpected RefImplExecuteBody() error: %q", err.Error()) @@ -537,7 +536,7 @@ func TestExecuteRequestSetters(t *testing.T) { func TestPrepareRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplPrepareBody(refEnc, validExpr) if err != nil { t.Errorf("An unexpected RefImplPrepareBody() error: %q", err.Error()) @@ -551,7 +550,7 @@ func TestPrepareRequestDefaultValues(t *testing.T) { func TestUnprepareRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplUnprepareBody(refEnc, *validStmt) if err != nil { t.Errorf("An unexpected RefImplUnprepareBody() error: %q", err.Error()) @@ -567,7 +566,7 @@ func TestExecutePreparedRequestSetters(t *testing.T) { args := []interface{}{uint(11)} var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplExecutePreparedBody(refEnc, *validStmt, args) if err != nil { t.Errorf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) @@ -583,7 +582,7 @@ func TestExecutePreparedRequestSetters(t *testing.T) { func TestExecutePreparedRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplExecutePreparedBody(refEnc, *validStmt, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) @@ -598,7 +597,7 @@ func TestExecutePreparedRequestDefaultValues(t *testing.T) { func TestBeginRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplBeginBody(refEnc, defaultIsolationLevel, defaultTimeout) if err != nil { t.Errorf("An unexpected RefImplBeginBody() error: %q", err.Error()) @@ -612,7 +611,7 @@ func TestBeginRequestDefaultValues(t *testing.T) { func TestBeginRequestSetters(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplBeginBody(refEnc, ReadConfirmedLevel, validTimeout) if err != nil { t.Errorf("An unexpected RefImplBeginBody() error: %q", err.Error()) @@ -626,7 +625,7 @@ func TestBeginRequestSetters(t *testing.T) { func TestCommitRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplCommitBody(refEnc) if err != nil { t.Errorf("An unexpected RefImplCommitBody() error: %q", err.Error()) @@ -640,7 +639,7 @@ func TestCommitRequestDefaultValues(t *testing.T) { func TestRollbackRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer - refEnc := msgpack.NewEncoder(&refBuf) + refEnc := NewEncoder(&refBuf) err := RefImplRollbackBody(refEnc) if err != nil { t.Errorf("An unexpected RefImplRollbackBody() error: %q", err.Error()) diff --git a/response.go b/response.go index 3fd7322b0..96b2c15fa 100644 --- a/response.go +++ b/response.go @@ -2,8 +2,6 @@ package tarantool import ( "fmt" - - "gopkg.in/vmihailenco/msgpack.v2" ) type Response struct { @@ -31,7 +29,7 @@ type SQLInfo struct { InfoAutoincrementIds []uint64 } -func (meta *ColumnMetaData) DecodeMsgpack(d *msgpack.Decoder) error { +func (meta *ColumnMetaData) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeMapLen(); err != nil { @@ -69,7 +67,7 @@ func (meta *ColumnMetaData) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (info *SQLInfo) DecodeMsgpack(d *msgpack.Decoder) error { +func (info *SQLInfo) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeMapLen(); err != nil { @@ -99,7 +97,7 @@ func (info *SQLInfo) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (resp *Response) smallInt(d *msgpack.Decoder) (i int, err error) { +func (resp *Response) smallInt(d *decoder) (i int, err error) { b, err := resp.buf.ReadByte() if err != nil { return @@ -111,7 +109,7 @@ func (resp *Response) smallInt(d *msgpack.Decoder) (i int, err error) { return d.DecodeInt() } -func (resp *Response) decodeHeader(d *msgpack.Decoder) (err error) { +func (resp *Response) decodeHeader(d *decoder) (err error) { var l int d.Reset(&resp.buf) if l, err = d.DecodeMapLen(); err != nil { @@ -148,7 +146,9 @@ func (resp *Response) decodeBody() (err error) { if resp.buf.Len() > 2 { var l int var stmtID, bindCount uint64 - d := msgpack.NewDecoder(&resp.buf) + + d := newDecoder(&resp.buf) + if l, err = d.DecodeMapLen(); err != nil { return err } @@ -212,7 +212,7 @@ func (resp *Response) decodeBody() (err error) { func (resp *Response) decodeBodyTyped(res interface{}) (err error) { if resp.buf.Len() > 0 { var l int - d := msgpack.NewDecoder(&resp.buf) + d := newDecoder(&resp.buf) if l, err = d.DecodeMapLen(); err != nil { return err } diff --git a/schema.go b/schema.go index fc3b0793a..ecf565762 100644 --- a/schema.go +++ b/schema.go @@ -3,8 +3,6 @@ package tarantool import ( "errors" "fmt" - "gopkg.in/vmihailenco/msgpack.v2" - msgpcode "gopkg.in/vmihailenco/msgpack.v2/codes" ) //nolint: varcheck,deadcode @@ -51,27 +49,7 @@ type Space struct { IndexesById map[uint32]*Index } -func isUint(code byte) bool { - return code == msgpcode.Uint8 || code == msgpcode.Uint16 || - code == msgpcode.Uint32 || code == msgpcode.Uint64 || - msgpcode.IsFixedNum(code) -} - -func isMap(code byte) bool { - return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code) -} - -func isArray(code byte) bool { - return code == msgpcode.Array16 || code == msgpcode.Array32 || - msgpcode.IsFixedArray(code) -} - -func isString(code byte) bool { - return msgpcode.IsFixedString(code) || code == msgpcode.Str8 || - code == msgpcode.Str16 || code == msgpcode.Str32 -} - -func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error { +func (space *Space) DecodeMsgpack(d *decoder) error { arrayLen, err := d.DecodeArrayLen() if err != nil { return err @@ -96,13 +74,13 @@ func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error { if err != nil { return err } - if isString(code) { + if msgpackIsString(code) { val, err := d.DecodeString() if err != nil { return err } space.Temporary = val == "temporary" - } else if isMap(code) { + } else if msgpackIsMap(code) { mapLen, err := d.DecodeMapLen() if err != nil { return err @@ -156,7 +134,7 @@ type Field struct { Type string } -func (field *Field) DecodeMsgpack(d *msgpack.Decoder) error { +func (field *Field) DecodeMsgpack(d *decoder) error { l, err := d.DecodeMapLen() if err != nil { return err @@ -194,7 +172,7 @@ type Index struct { Fields []*IndexField } -func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { +func (index *Index) DecodeMsgpack(d *decoder) error { _, err := d.DecodeArrayLen() if err != nil { return err @@ -218,7 +196,7 @@ func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { return err } - if isUint(code) { + if msgpackIsUint(code) { optsUint64, err := d.DecodeUint64() if err != nil { return nil @@ -241,7 +219,7 @@ func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { return err } - if isUint(code) { + if msgpackIsUint(code) { fieldCount, err := d.DecodeUint64() if err != nil { return err @@ -270,13 +248,13 @@ type IndexField struct { Type string } -func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { +func (indexField *IndexField) DecodeMsgpack(d *decoder) error { code, err := d.PeekCode() if err != nil { return err } - if isMap(code) { + if msgpackIsMap(code) { mapLen, err := d.DecodeMapLen() if err != nil { return err @@ -302,7 +280,7 @@ func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { } } return nil - } else if isArray(code) { + } else if msgpackIsArray(code) { arrayLen, err := d.DecodeArrayLen() if err != nil { return err diff --git a/stream.go b/stream.go index 62ea0adf0..009f6c0aa 100644 --- a/stream.go +++ b/stream.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "time" - - "gopkg.in/vmihailenco/msgpack.v2" ) type TxnIsolationLevel uint @@ -29,7 +27,7 @@ type Stream struct { Conn *Connection } -func fillBegin(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout time.Duration) error { +func fillBegin(enc *encoder, txnIsolation TxnIsolationLevel, timeout time.Duration) error { hasTimeout := timeout > 0 hasIsolationLevel := txnIsolation != DefaultIsolationLevel mapLen := 0 @@ -72,11 +70,11 @@ func fillBegin(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout tim return err } -func fillCommit(enc *msgpack.Encoder) error { +func fillCommit(enc *encoder) error { return enc.EncodeMapLen(0) } -func fillRollback(enc *msgpack.Encoder) error { +func fillRollback(enc *encoder) error { return enc.EncodeMapLen(0) } @@ -111,7 +109,7 @@ func (req *BeginRequest) Timeout(timeout time.Duration) *BeginRequest { } // Body fills an encoder with the begin request body. -func (req *BeginRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *BeginRequest) Body(res SchemaResolver, enc *encoder) error { return fillBegin(enc, req.txnIsolation, req.timeout) } @@ -141,7 +139,7 @@ func NewCommitRequest() *CommitRequest { } // Body fills an encoder with the commit request body. -func (req *CommitRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *CommitRequest) Body(res SchemaResolver, enc *encoder) error { return fillCommit(enc) } @@ -171,7 +169,7 @@ func NewRollbackRequest() *RollbackRequest { } // Body fills an encoder with the rollback request body. -func (req *RollbackRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { +func (req *RollbackRequest) Body(res SchemaResolver, enc *encoder) error { return fillRollback(enc) } diff --git a/tarantool_test.go b/tarantool_test.go index f3fdbb787..ea0bc784d 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/assert" . "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/test_helpers" - "gopkg.in/vmihailenco/msgpack.v2" ) type Member struct { @@ -26,7 +25,7 @@ type Member struct { Val uint } -func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { +func (m *Member) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(2); err != nil { return err } @@ -39,7 +38,7 @@ func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { return nil } -func (m *Member) DecodeMsgpack(d *msgpack.Decoder) error { +func (m *Member) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { diff --git a/test_helpers/msgpack.go b/test_helpers/msgpack.go new file mode 100644 index 000000000..f18b17f48 --- /dev/null +++ b/test_helpers/msgpack.go @@ -0,0 +1,7 @@ +package test_helpers + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder diff --git a/test_helpers/request_mock.go b/test_helpers/request_mock.go index 630d57e66..93551e34a 100644 --- a/test_helpers/request_mock.go +++ b/test_helpers/request_mock.go @@ -4,7 +4,6 @@ import ( "context" "github.com/tarantool/go-tarantool" - "gopkg.in/vmihailenco/msgpack.v2" ) type StrangerRequest struct { @@ -18,7 +17,7 @@ func (sr *StrangerRequest) Code() int32 { return 0 } -func (sr *StrangerRequest) Body(resolver tarantool.SchemaResolver, enc *msgpack.Encoder) error { +func (sr *StrangerRequest) Body(resolver tarantool.SchemaResolver, enc *encoder) error { return nil } diff --git a/uuid/msgpack.go b/uuid/msgpack.go new file mode 100644 index 000000000..1fcbc711b --- /dev/null +++ b/uuid/msgpack.go @@ -0,0 +1,16 @@ +package uuid + +import ( + "reflect" + + "github.com/google/uuid" + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func init() { + msgpack.Register(reflect.TypeOf((*uuid.UUID)(nil)).Elem(), encodeUUID, decodeUUID) + msgpack.RegisterExt(UUID_extId, (*uuid.UUID)(nil)) +} diff --git a/uuid/msgpack_helper_test.go b/uuid/msgpack_helper_test.go new file mode 100644 index 000000000..e8550f050 --- /dev/null +++ b/uuid/msgpack_helper_test.go @@ -0,0 +1,7 @@ +package uuid_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type decoder = msgpack.Decoder diff --git a/uuid/uuid.go b/uuid/uuid.go index 7d362c3d8..a35f33faf 100644 --- a/uuid/uuid.go +++ b/uuid/uuid.go @@ -18,13 +18,12 @@ import ( "reflect" "github.com/google/uuid" - "gopkg.in/vmihailenco/msgpack.v2" ) // UUID external type. const UUID_extId = 2 -func encodeUUID(e *msgpack.Encoder, v reflect.Value) error { +func encodeUUID(e *encoder, v reflect.Value) error { id := v.Interface().(uuid.UUID) bytes, err := id.MarshalBinary() @@ -40,7 +39,7 @@ func encodeUUID(e *msgpack.Encoder, v reflect.Value) error { return nil } -func decodeUUID(d *msgpack.Decoder, v reflect.Value) error { +func decodeUUID(d *decoder, v reflect.Value) error { var bytesCount int = 16 bytes := make([]byte, bytesCount) @@ -60,8 +59,3 @@ func decodeUUID(d *msgpack.Decoder, v reflect.Value) error { v.Set(reflect.ValueOf(id)) return nil } - -func init() { - msgpack.Register(reflect.TypeOf((*uuid.UUID)(nil)).Elem(), encodeUUID, decodeUUID) - msgpack.RegisterExt(UUID_extId, (*uuid.UUID)(nil)) -} diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index aed760009..6224a938b 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -11,7 +11,6 @@ import ( . "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/test_helpers" _ "github.com/tarantool/go-tarantool/uuid" - "gopkg.in/vmihailenco/msgpack.v2" ) // There is no way to skip tests in testing.M, @@ -33,7 +32,7 @@ type TupleUUID struct { id uuid.UUID } -func (t *TupleUUID) DecodeMsgpack(d *msgpack.Decoder) error { +func (t *TupleUUID) DecodeMsgpack(d *decoder) error { var err error var l int if l, err = d.DecodeArrayLen(); err != nil { From fa5d40f0137ecea52e616fb7ca2b09112e65dca2 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Wed, 18 May 2022 17:37:21 +0300 Subject: [PATCH 5/7] msgpack: add wrappers for EncodeUint/EncodeInt EncodeUint and EncodeInt have different argument types in msgpack.v2[1][2] and msgpack.v5[3][4]. The wrappers will simplify migration to msgpack.v5. 1. https://pkg.go.dev/github.com/vmihailenco/msgpack@v2.9.2+incompatible#Encoder.EncodeUint 2. https://pkg.go.dev/github.com/vmihailenco/msgpack@v2.9.2+incompatible#Encoder.EncodeInt 3. https://pkg.go.dev/github.com/vmihailenco/msgpack/v5#Encoder.EncodeUint 4. https://pkg.go.dev/github.com/vmihailenco/msgpack/v5#Encoder.EncodeInt Part of #124 --- client_tools.go | 16 +++++----- datetime/datetime_test.go | 2 +- datetime/msgpack_helper_test.go | 4 +++ example_custom_unpacking_test.go | 2 +- msgpack.go | 8 +++++ msgpack_helper_test.go | 4 +++ prepared.go | 12 ++++---- request.go | 50 ++++++++++++++++---------------- stream.go | 6 ++-- tarantool_test.go | 2 +- 10 files changed, 61 insertions(+), 45 deletions(-) diff --git a/client_tools.go b/client_tools.go index 88da9c577..a5d6fbba9 100644 --- a/client_tools.go +++ b/client_tools.go @@ -8,7 +8,7 @@ type IntKey struct { func (k IntKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(1) - enc.EncodeInt(k.I) + encodeInt(enc, int64(k.I)) return nil } @@ -20,7 +20,7 @@ type UintKey struct { func (k UintKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(1) - enc.EncodeUint(k.I) + encodeUint(enc, uint64(k.I)) return nil } @@ -44,8 +44,8 @@ type IntIntKey struct { func (k IntIntKey) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(2) - enc.EncodeInt(k.I1) - enc.EncodeInt(k.I2) + encodeInt(enc, int64(k.I1)) + encodeInt(enc, int64(k.I2)) return nil } @@ -59,7 +59,7 @@ type Op struct { func (o Op) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(3) enc.EncodeString(o.Op) - enc.EncodeInt(o.Field) + encodeInt(enc, int64(o.Field)) return enc.Encode(o.Arg) } @@ -147,9 +147,9 @@ type OpSplice struct { func (o OpSplice) EncodeMsgpack(enc *encoder) error { enc.EncodeArrayLen(5) enc.EncodeString(o.Op) - enc.EncodeInt(o.Field) - enc.EncodeInt(o.Pos) - enc.EncodeInt(o.Len) + encodeInt(enc, int64(o.Field)) + encodeInt(enc, int64(o.Pos)) + encodeInt(enc, int64(o.Len)) enc.EncodeString(o.Replace) return nil } diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 035b1025b..176f8a40c 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -332,7 +332,7 @@ func (c *Tuple2) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(3); err != nil { return err } - if err := e.EncodeUint(c.Cid); err != nil { + if err := encodeUint(e, uint64(c.Cid)); err != nil { return err } if err := e.EncodeString(c.Orig); err != nil { diff --git a/datetime/msgpack_helper_test.go b/datetime/msgpack_helper_test.go index ba283db18..7b045a863 100644 --- a/datetime/msgpack_helper_test.go +++ b/datetime/msgpack_helper_test.go @@ -7,6 +7,10 @@ import ( type encoder = msgpack.Encoder type decoder = msgpack.Decoder +func encodeUint(e *encoder, v uint64) error { + return e.EncodeUint(uint(v)) +} + func marshal(v interface{}) ([]byte, error) { return msgpack.Marshal(v) } diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index cff50da50..4087c5620 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -27,7 +27,7 @@ func (c *Tuple2) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(3); err != nil { return err } - if err := e.EncodeUint(c.Cid); err != nil { + if err := encodeUint(e, uint64(c.Cid)); err != nil { return err } if err := e.EncodeString(c.Orig); err != nil { diff --git a/msgpack.go b/msgpack.go index bcdcc06ce..e48e14a21 100644 --- a/msgpack.go +++ b/msgpack.go @@ -18,6 +18,14 @@ func newDecoder(r io.Reader) *decoder { return msgpack.NewDecoder(r) } +func encodeUint(e *encoder, v uint64) error { + return e.EncodeUint(uint(v)) +} + +func encodeInt(e *encoder, v int64) error { + return e.EncodeInt(int(v)) +} + func msgpackIsUint(code byte) bool { return code == msgpcode.Uint8 || code == msgpcode.Uint16 || code == msgpcode.Uint32 || code == msgpcode.Uint64 || diff --git a/msgpack_helper_test.go b/msgpack_helper_test.go index 8623059e5..fcadbc573 100644 --- a/msgpack_helper_test.go +++ b/msgpack_helper_test.go @@ -6,3 +6,7 @@ import ( type encoder = msgpack.Encoder type decoder = msgpack.Decoder + +func encodeUint(e *encoder, v uint64) error { + return e.EncodeUint(uint(v)) +} diff --git a/prepared.go b/prepared.go index 91c5238a7..6f63e3bb1 100644 --- a/prepared.go +++ b/prepared.go @@ -20,21 +20,21 @@ type Prepared struct { func fillPrepare(enc *encoder, expr string) error { enc.EncodeMapLen(1) - enc.EncodeUint(KeySQLText) + encodeUint(enc, KeySQLText) return enc.EncodeString(expr) } func fillUnprepare(enc *encoder, stmt Prepared) error { enc.EncodeMapLen(1) - enc.EncodeUint(KeyStmtID) - return enc.EncodeUint(uint(stmt.StatementID)) + encodeUint(enc, KeyStmtID) + return encodeUint(enc, uint64(stmt.StatementID)) } func fillExecutePrepared(enc *encoder, stmt Prepared, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint(KeyStmtID) - enc.EncodeUint(uint(stmt.StatementID)) - enc.EncodeUint(KeySQLBind) + encodeUint(enc, KeyStmtID) + encodeUint(enc, uint64(stmt.StatementID)) + encodeUint(enc, KeySQLBind) return encodeSQLBind(enc, args) } diff --git a/request.go b/request.go index 625380306..cfa40e522 100644 --- a/request.go +++ b/request.go @@ -9,28 +9,28 @@ import ( ) func fillSearch(enc *encoder, spaceNo, indexNo uint32, key interface{}) error { - enc.EncodeUint(KeySpaceNo) - enc.EncodeUint(uint(spaceNo)) - enc.EncodeUint(KeyIndexNo) - enc.EncodeUint(uint(indexNo)) - enc.EncodeUint(KeyKey) + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyIndexNo) + encodeUint(enc, uint64(indexNo)) + encodeUint(enc, KeyKey) return enc.Encode(key) } func fillIterator(enc *encoder, offset, limit, iterator uint32) { - enc.EncodeUint(KeyIterator) - enc.EncodeUint(uint(iterator)) - enc.EncodeUint(KeyOffset) - enc.EncodeUint(uint(offset)) - enc.EncodeUint(KeyLimit) - enc.EncodeUint(uint(limit)) + encodeUint(enc, KeyIterator) + encodeUint(enc, uint64(iterator)) + encodeUint(enc, KeyOffset) + encodeUint(enc, uint64(offset)) + encodeUint(enc, KeyLimit) + encodeUint(enc, uint64(limit)) } func fillInsert(enc *encoder, spaceNo uint32, tuple interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint(KeySpaceNo) - enc.EncodeUint(uint(spaceNo)) - enc.EncodeUint(KeyTuple) + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyTuple) return enc.Encode(tuple) } @@ -45,19 +45,19 @@ func fillUpdate(enc *encoder, spaceNo, indexNo uint32, key, ops interface{}) err if err := fillSearch(enc, spaceNo, indexNo, key); err != nil { return err } - enc.EncodeUint(KeyTuple) + encodeUint(enc, KeyTuple) return enc.Encode(ops) } func fillUpsert(enc *encoder, spaceNo uint32, tuple, ops interface{}) error { enc.EncodeMapLen(3) - enc.EncodeUint(KeySpaceNo) - enc.EncodeUint(uint(spaceNo)) - enc.EncodeUint(KeyTuple) + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyTuple) if err := enc.Encode(tuple); err != nil { return err } - enc.EncodeUint(KeyDefTuple) + encodeUint(enc, KeyDefTuple) return enc.Encode(ops) } @@ -68,25 +68,25 @@ func fillDelete(enc *encoder, spaceNo, indexNo uint32, key interface{}) error { func fillCall(enc *encoder, functionName string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint(KeyFunctionName) + encodeUint(enc, KeyFunctionName) enc.EncodeString(functionName) - enc.EncodeUint(KeyTuple) + encodeUint(enc, KeyTuple) return enc.Encode(args) } func fillEval(enc *encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint(KeyExpression) + encodeUint(enc, KeyExpression) enc.EncodeString(expr) - enc.EncodeUint(KeyTuple) + encodeUint(enc, KeyTuple) return enc.Encode(args) } func fillExecute(enc *encoder, expr string, args interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint(KeySQLText) + encodeUint(enc, KeySQLText) enc.EncodeString(expr) - enc.EncodeUint(KeySQLBind) + encodeUint(enc, KeySQLBind) return encodeSQLBind(enc, args) } diff --git a/stream.go b/stream.go index 009f6c0aa..3a03ec68f 100644 --- a/stream.go +++ b/stream.go @@ -44,7 +44,7 @@ func fillBegin(enc *encoder, txnIsolation TxnIsolationLevel, timeout time.Durati } if hasTimeout { - err = enc.EncodeUint(KeyTimeout) + err = encodeUint(enc, KeyTimeout) if err != nil { return err } @@ -56,12 +56,12 @@ func fillBegin(enc *encoder, txnIsolation TxnIsolationLevel, timeout time.Durati } if hasIsolationLevel { - err = enc.EncodeUint(KeyTxnIsolation) + err = encodeUint(enc, KeyTxnIsolation) if err != nil { return err } - err = enc.Encode(txnIsolation) + err = encodeUint(enc, uint64(txnIsolation)) if err != nil { return err } diff --git a/tarantool_test.go b/tarantool_test.go index ea0bc784d..18ebb0506 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -32,7 +32,7 @@ func (m *Member) EncodeMsgpack(e *encoder) error { if err := e.EncodeString(m.Name); err != nil { return err } - if err := e.EncodeUint(m.Val); err != nil { + if err := encodeUint(e, uint64(m.Val)); err != nil { return err } return nil From 178f15c18db2fac066631f29974cd969562b8914 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Wed, 18 May 2022 18:00:12 +0300 Subject: [PATCH 6/7] msgpack: add optional msgpack.v5 support The msgpack.v5 code located in msgpack_v5.go and msgpack_v5_helper_test.go for tests. It is the same logic for submodules. An user can use msgpack.v5 with a build tag: go_tarantool_msgpack_v5 The commit also unify typo conversions after decoding in tests. This is necessary because msgpack v2.9.2 decodes all numbers as uint[1] or int[2], but msgpack.v5 decodes numbers as a MessagePack type[3]. There are additional differences when decoding types. 1. https://github.com/vmihailenco/msgpack/blob/23644a15054d8b9f8a9fca3e041f3419504b9c21/decode.go#L283 2. https://github.com/vmihailenco/msgpack/blob/23644a15054d8b9f8a9fca3e041f3419504b9c21/decode.go#L285 3. https://github.com/vmihailenco/msgpack/blob/233c977ae92b215a9aa7eb420bbe67da99f05ab6/decode.go#L410 Part of #124 --- CHANGELOG.md | 2 + README.md | 36 +++++++- call_16_test.go | 10 +-- call_17_test.go | 10 +-- config.lua | 8 +- datetime/datetime_test.go | 17 ++-- datetime/msgpack.go | 3 + datetime/msgpack_helper_test.go | 10 ++- datetime/msgpack_v5.go | 12 +++ datetime/msgpack_v5_helper_test.go | 28 +++++++ decimal/decimal_test.go | 13 ++- decimal/msgpack.go | 3 + decimal/msgpack_helper_test.go | 10 +++ decimal/msgpack_v5.go | 12 +++ decimal/msgpack_v5_helper_test.go | 28 +++++++ example_test.go | 14 ++-- go.mod | 1 + go.sum | 5 ++ msgpack.go | 3 + msgpack_helper_test.go | 3 + msgpack_v5.go | 54 ++++++++++++ msgpack_v5_helper_test.go | 15 ++++ multi/call_16_test.go | 4 +- multi/call_17_test.go | 4 +- multi/config.lua | 8 +- multi/multi_test.go | 22 ++--- queue/msgpack.go | 3 + queue/msgpack_helper_test.go | 3 + queue/msgpack_v5.go | 10 +++ queue/msgpack_v5_helper_test.go | 11 +++ queue/queue.go | 25 ++++-- tarantool_test.go | 127 ++++++++++++++++++----------- test_helpers/msgpack.go | 3 + test_helpers/msgpack_v5.go | 10 +++ uuid/msgpack.go | 3 + uuid/msgpack_helper_test.go | 3 + uuid/msgpack_v5.go | 27 ++++++ uuid/msgpack_v5_helper_test.go | 10 +++ 38 files changed, 461 insertions(+), 109 deletions(-) create mode 100644 datetime/msgpack_v5.go create mode 100644 datetime/msgpack_v5_helper_test.go create mode 100644 decimal/msgpack_v5.go create mode 100644 decimal/msgpack_v5_helper_test.go create mode 100644 msgpack_v5.go create mode 100644 msgpack_v5_helper_test.go create mode 100644 queue/msgpack_v5.go create mode 100644 queue/msgpack_v5_helper_test.go create mode 100644 test_helpers/msgpack_v5.go create mode 100644 uuid/msgpack_v5.go create mode 100644 uuid/msgpack_v5_helper_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 89d5fe5a7..6381b3d13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. ### Added +- Optional msgpack.v5 usage (#124) + ### Changed ### Fixed diff --git a/README.md b/README.md index 0d992a344..769acea21 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ faster than other packages according to public benchmarks. * [Documentation](#documentation) * [API reference](#api-reference) * [Walking\-through example](#walking-through-example) + * [msgpack.v5 migration](#msgpackv5-migration) * [Contributing](#contributing) * [Alternative connectors](#alternative-connectors) @@ -68,7 +69,15 @@ This allows us to introduce new features without losing backward compatibility. go_tarantool_call_17 ``` **Note:** In future releases, `Call17` may be used as default `Call` behavior. -3. To run fuzz tests with decimals, you can use the build tag: +3. To replace usage of `msgpack.v2` with `msgpack.v5`, you can use the build + tag: + ``` + go_tarantool_msgpack_v5 + ``` + **Note:** In future releases, `msgpack.v5` may be used by default. We recommend + to read [msgpack.v5 migration notes](#msgpackv5-migration) and try to + use msgpack.v5 before the changes. +4. To run fuzz tests with decimals, you can use the build tag: ``` go_tarantool_decimal_fuzzing ``` @@ -144,6 +153,31 @@ There are two parameters: * a space number (it could just as easily have been a space name), and * a tuple. +### msgpack.v5 migration + +Most function names and argument types in `msgpack.v5` and `msgpack.v2` +have not changed (in our code, we noticed changes in `EncodeInt`, `EncodeUint` +and `RegisterExt`). But there are a lot of changes in a logic of encoding and +decoding. On the plus side the migration seems easy, but on the minus side you +need to be very careful. + +First of all, `EncodeInt8`, `EncodeInt16`, `EncodeInt32`, `EncodeInt64` +and `EncodeUint*` analogues at `msgpack.v5` encode numbers as is without loss of +type. In `msgpack.v2` the type of a number is reduced to a value. + +Secondly, a base decoding function does not convert numbers to `int64` or +`uint64`. It converts numbers to an exact type defined by MessagePack. The +change makes manual type conversions much more difficult and can lead to +runtime errors with an old code. We do not recommend to use type conversions +and give preference to `*Typed` functions (besides, it's faster). + +There are also changes in the logic that can lead to errors in the old code, +[as example](https://github.com/vmihailenco/msgpack/issues/327). Although in +`msgpack.v5` some functions for the logic tuning were added (see +[UseLooseInterfaceDecoding](https://pkg.go.dev/github.com/vmihailenco/msgpack/v5#Decoder.UseLooseInterfaceDecoding), [UseCompactInts](https://pkg.go.dev/github.com/vmihailenco/msgpack/v5#Encoder.UseCompactInts) etc), it is still impossible +to achieve full compliance of behavior between `msgpack.v5` and `msgpack.v2`. So +we don't go this way. We use standart settings if it possible. + ## Contributing See [the contributing guide](CONTRIBUTING.md) for detailed instructions on how diff --git a/call_16_test.go b/call_16_test.go index 1dab3079d..f23f0f783 100644 --- a/call_16_test.go +++ b/call_16_test.go @@ -18,11 +18,11 @@ func TestConnection_Call(t *testing.T) { defer conn.Close() // Call16 - resp, err = conn.Call("simple_incr", []interface{}{1}) + resp, err = conn.Call("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].([]interface{})[0].(uint64) != 2 { + if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } } @@ -34,18 +34,18 @@ func TestCallRequest(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, server, opts) defer conn.Close() - req := NewCallRequest("simple_incr").Args([]interface{}{1}) + req := NewCallRequest("simple_concat").Args([]interface{}{"1"}) resp, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].([]interface{})[0].(uint64) != 2 { + if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } } func TestCallRequestCode(t *testing.T) { - req := NewCallRequest("simple_incrt") + req := NewCallRequest("simple_concat") code := req.Code() expected := Call16RequestCode if code != int32(expected) { diff --git a/call_17_test.go b/call_17_test.go index 400c69693..824ed850b 100644 --- a/call_17_test.go +++ b/call_17_test.go @@ -18,11 +18,11 @@ func TestConnection_Call(t *testing.T) { defer conn.Close() // Call17 - resp, err = conn.Call17("simple_incr", []interface{}{1}) + resp, err = conn.Call17("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].(uint64) != 2 { + if val, ok := resp.Data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } } @@ -34,18 +34,18 @@ func TestCallRequest(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, server, opts) defer conn.Close() - req := NewCallRequest("simple_incr").Args([]interface{}{1}) + req := NewCallRequest("simple_concat").Args([]interface{}{"1"}) resp, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].(uint64) != 2 { + if val, ok := resp.Data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } } func TestCallRequestCode(t *testing.T) { - req := NewCallRequest("simple_incrt") + req := NewCallRequest("simple_concat") code := req.Code() expected := Call17RequestCode if code != int32(expected) { diff --git a/config.lua b/config.lua index 9ec12f7d5..db4aed6eb 100644 --- a/config.lua +++ b/config.lua @@ -80,7 +80,7 @@ box.once("init", function() --box.schema.user.grant('guest', 'read,write,execute', 'universe') box.schema.func.create('box.info') - box.schema.func.create('simple_incr') + box.schema.func.create('simple_concat') -- auth testing: access control box.schema.user.create('test', {password = 'test'}) @@ -106,10 +106,10 @@ local function func_name() end rawset(_G, 'func_name', func_name) -local function simple_incr(a) - return a + 1 +local function simple_concat(a) + return a .. a end -rawset(_G, 'simple_incr', simple_incr) +rawset(_G, 'simple_concat', simple_concat) local function push_func(cnt) for i = 1, cnt do diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 176f8a40c..7d9dbe29a 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -64,10 +64,10 @@ func assertDatetimeIsEqual(t *testing.T, tuples []interface{}, tm time.Time) { if len(tpl) != 2 { t.Fatalf("Unexpected return value body (tuple len = %d)", len(tpl)) } - if val, ok := tpl[dtIndex].(Datetime); !ok || !val.ToTime().Equal(tm) { + if val, ok := toDatetime(tpl[dtIndex]); !ok || !val.ToTime().Equal(tm) { t.Fatalf("Unexpected tuple %d field %v, expected %v", dtIndex, - val.ToTime(), + val, tm) } } @@ -324,7 +324,10 @@ func (ev *Event) DecodeMsgpack(d *decoder) error { if err != nil { return err } - ev.Datetime = res.(Datetime) + var ok bool + if ev.Datetime, ok = toDatetime(res); !ok { + return fmt.Errorf("datetime doesn't match") + } return nil } @@ -332,7 +335,7 @@ func (c *Tuple2) EncodeMsgpack(e *encoder) error { if err := e.EncodeArrayLen(3); err != nil { return err } - if err := encodeUint(e, uint64(c.Cid)); err != nil { + if err := e.EncodeUint64(uint64(c.Cid)); err != nil { return err } if err := e.EncodeString(c.Orig); err != nil { @@ -428,8 +431,8 @@ func TestCustomEncodeDecodeTuple1(t *testing.T) { } for i, tv := range []time.Time{tm1, tm2} { - dt := events[i].([]interface{})[1].(Datetime) - if !dt.ToTime().Equal(tv) { + dt, ok := toDatetime(events[i].([]interface{})[1]) + if !ok || !dt.ToTime().Equal(tv) { t.Fatalf("%v != %v", dt.ToTime(), tv) } } @@ -501,7 +504,7 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { - if val, ok := tpl[0].(Datetime); !ok || !val.ToTime().Equal(tm) { + if val, ok := toDatetime(tpl[0]); !ok || !val.ToTime().Equal(tm) { t.Fatalf("Unexpected body of Select") } } diff --git a/datetime/msgpack.go b/datetime/msgpack.go index 915c541ec..4f48f1d3e 100644 --- a/datetime/msgpack.go +++ b/datetime/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package datetime import ( diff --git a/datetime/msgpack_helper_test.go b/datetime/msgpack_helper_test.go index 7b045a863..7af2ee6ad 100644 --- a/datetime/msgpack_helper_test.go +++ b/datetime/msgpack_helper_test.go @@ -1,14 +1,20 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package datetime_test import ( + . "github.com/tarantool/go-tarantool/datetime" + "gopkg.in/vmihailenco/msgpack.v2" ) type encoder = msgpack.Encoder type decoder = msgpack.Decoder -func encodeUint(e *encoder, v uint64) error { - return e.EncodeUint(uint(v)) +func toDatetime(i interface{}) (dt Datetime, ok bool) { + dt, ok = i.(Datetime) + return } func marshal(v interface{}) ([]byte, error) { diff --git a/datetime/msgpack_v5.go b/datetime/msgpack_v5.go new file mode 100644 index 000000000..a69a81aa3 --- /dev/null +++ b/datetime/msgpack_v5.go @@ -0,0 +1,12 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package datetime + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +func init() { + msgpack.RegisterExt(datetime_extId, (*Datetime)(nil)) +} diff --git a/datetime/msgpack_v5_helper_test.go b/datetime/msgpack_v5_helper_test.go new file mode 100644 index 000000000..d750ef006 --- /dev/null +++ b/datetime/msgpack_v5_helper_test.go @@ -0,0 +1,28 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package datetime_test + +import ( + . "github.com/tarantool/go-tarantool/datetime" + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func toDatetime(i interface{}) (dt Datetime, ok bool) { + var ptr *Datetime + if ptr, ok = i.(*Datetime); ok { + dt = *ptr + } + return +} + +func marshal(v interface{}) ([]byte, error) { + return msgpack.Marshal(v) +} + +func unmarshal(data []byte, v interface{}) error { + return msgpack.Unmarshal(data, v) +} diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go index 9eb056ca8..6fba0cc8f 100644 --- a/decimal/decimal_test.go +++ b/decimal/decimal_test.go @@ -60,8 +60,10 @@ func (t *TupleDecimal) DecodeMsgpack(d *decoder) error { if err != nil { return err } - t.number = res.(Decimal) - + var ok bool + if t.number, ok = toDecimal(res); !ok { + return fmt.Errorf("decimal doesn't match") + } return nil } @@ -347,7 +349,7 @@ func tupleValueIsDecimal(t *testing.T, tuples []interface{}, number decimal.Deci if len(tpl) != 1 { t.Fatalf("Unexpected return value body (tuple len)") } - if val, ok := tpl[0].(Decimal); !ok || !val.Equal(number) { + if val, ok := toDecimal(tpl[0]); !ok || !val.Equal(number) { t.Fatalf("Unexpected return value body (tuple 0 field)") } } @@ -445,7 +447,10 @@ func TestMPDecode(t *testing.T) { if err != nil { t.Fatalf("Unmarshalling failed: %s", err.Error()) } - decActual := v.(Decimal) + decActual, ok := toDecimal(v) + if !ok { + t.Fatalf("Unable to convert to Decimal") + } decExpected, err := decimal.NewFromString(testcase.numString) if err != nil { diff --git a/decimal/msgpack.go b/decimal/msgpack.go index ca360172f..5a455ae59 100644 --- a/decimal/msgpack.go +++ b/decimal/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package decimal import ( diff --git a/decimal/msgpack_helper_test.go b/decimal/msgpack_helper_test.go index 3013f4cf0..3824f70b8 100644 --- a/decimal/msgpack_helper_test.go +++ b/decimal/msgpack_helper_test.go @@ -1,12 +1,22 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package decimal_test import ( + . "github.com/tarantool/go-tarantool/decimal" + "gopkg.in/vmihailenco/msgpack.v2" ) type encoder = msgpack.Encoder type decoder = msgpack.Decoder +func toDecimal(i interface{}) (dec Decimal, ok bool) { + dec, ok = i.(Decimal) + return +} + func marshal(v interface{}) ([]byte, error) { return msgpack.Marshal(v) } diff --git a/decimal/msgpack_v5.go b/decimal/msgpack_v5.go new file mode 100644 index 000000000..59fa713d0 --- /dev/null +++ b/decimal/msgpack_v5.go @@ -0,0 +1,12 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package decimal + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +func init() { + msgpack.RegisterExt(decimalExtID, (*Decimal)(nil)) +} diff --git a/decimal/msgpack_v5_helper_test.go b/decimal/msgpack_v5_helper_test.go new file mode 100644 index 000000000..6bb78168a --- /dev/null +++ b/decimal/msgpack_v5_helper_test.go @@ -0,0 +1,28 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package decimal_test + +import ( + . "github.com/tarantool/go-tarantool/decimal" + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func toDecimal(i interface{}) (dec Decimal, ok bool) { + var ptr *Decimal + if ptr, ok = i.(*Decimal); ok { + dec = *ptr + } + return +} + +func marshal(v interface{}) ([]byte, error) { + return msgpack.Marshal(v) +} + +func unmarshal(data []byte, v interface{}) error { + return msgpack.Unmarshal(data, v) +} diff --git a/example_test.go b/example_test.go index df7dad770..88779bc47 100644 --- a/example_test.go +++ b/example_test.go @@ -471,10 +471,10 @@ func ExampleFuture_GetIterator() { resp := it.Value() if resp.Code == tarantool.PushCode { // It is a push message. - fmt.Printf("push message: %d\n", resp.Data[0].(uint64)) + fmt.Printf("push message: %v\n", resp.Data[0]) } else if resp.Code == tarantool.OkCode { // It is a regular response. - fmt.Printf("response: %d", resp.Data[0].(uint64)) + fmt.Printf("response: %v", resp.Data[0]) } else { fmt.Printf("an unexpected response code %d", resp.Code) } @@ -645,17 +645,17 @@ func ExampleConnection_Call() { conn := example_connect() defer conn.Close() - // Call a function 'simple_incr' with arguments. - resp, err := conn.Call17("simple_incr", []interface{}{1}) - fmt.Println("Call simple_incr()") + // Call a function 'simple_concat' with arguments. + resp, err := conn.Call17("simple_concat", []interface{}{"1"}) + fmt.Println("Call simple_concat()") fmt.Println("Error", err) fmt.Println("Code", resp.Code) fmt.Println("Data", resp.Data) // Output: - // Call simple_incr() + // Call simple_concat() // Error // Code 0 - // Data [2] + // Data [11] } func ExampleConnection_Eval() { diff --git a/go.mod b/go.mod index c10d60795..2f3b8c226 100644 --- a/go.mod +++ b/go.mod @@ -14,4 +14,5 @@ require ( gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/vmihailenco/msgpack.v2 v2.9.2 gotest.tools/v3 v3.2.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 ) diff --git a/go.sum b/go.sum index fb3474afc..38e70ec44 100644 --- a/go.sum +++ b/go.sum @@ -24,10 +24,15 @@ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tarantool/go-openssl v0.0.8-0.20220711094538-d93c1eff4f49 h1:rZYYi1cI3QXZ3yRFZd2ItYM1XA2BaJqP0buDroMbjNo= github.com/tarantool/go-openssl v0.0.8-0.20220711094538-d93c1eff4f49/go.mod h1:M7H4xYSbzqpW/ZRBMyH0eyqQBsnhAMfsYk5mv0yid7A= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/msgpack.go b/msgpack.go index e48e14a21..34ecc4b3b 100644 --- a/msgpack.go +++ b/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package tarantool import ( diff --git a/msgpack_helper_test.go b/msgpack_helper_test.go index fcadbc573..fa47c2fda 100644 --- a/msgpack_helper_test.go +++ b/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package tarantool_test import ( diff --git a/msgpack_v5.go b/msgpack_v5.go new file mode 100644 index 000000000..806dd1632 --- /dev/null +++ b/msgpack_v5.go @@ -0,0 +1,54 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package tarantool + +import ( + "io" + + "github.com/vmihailenco/msgpack/v5" + "github.com/vmihailenco/msgpack/v5/msgpcode" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func newEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} + +func newDecoder(r io.Reader) *decoder { + dec := msgpack.NewDecoder(r) + dec.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) + return dec +} + +func encodeUint(e *encoder, v uint64) error { + return e.EncodeUint(v) +} + +func encodeInt(e *encoder, v int64) error { + return e.EncodeInt(v) +} + +func msgpackIsUint(code byte) bool { + return code == msgpcode.Uint8 || code == msgpcode.Uint16 || + code == msgpcode.Uint32 || code == msgpcode.Uint64 || + msgpcode.IsFixedNum(code) +} + +func msgpackIsMap(code byte) bool { + return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code) +} + +func msgpackIsArray(code byte) bool { + return code == msgpcode.Array16 || code == msgpcode.Array32 || + msgpcode.IsFixedArray(code) +} + +func msgpackIsString(code byte) bool { + return msgpcode.IsFixedString(code) || code == msgpcode.Str8 || + code == msgpcode.Str16 || code == msgpcode.Str32 +} diff --git a/msgpack_v5_helper_test.go b/msgpack_v5_helper_test.go new file mode 100644 index 000000000..347c1ba95 --- /dev/null +++ b/msgpack_v5_helper_test.go @@ -0,0 +1,15 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package tarantool_test + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func encodeUint(e *encoder, v uint64) error { + return e.EncodeUint(v) +} diff --git a/multi/call_16_test.go b/multi/call_16_test.go index 11a1a766d..35bcd20fb 100644 --- a/multi/call_16_test.go +++ b/multi/call_16_test.go @@ -23,11 +23,11 @@ func TestCall(t *testing.T) { defer multiConn.Close() // Call16 - resp, err = multiConn.Call("simple_incr", []interface{}{1}) + resp, err = multiConn.Call("simple_concat", []interface{}{"t"}) if err != nil { t.Fatalf("Failed to use Call: %s", err.Error()) } - if resp.Data[0].([]interface{})[0].(uint64) != 2 { + if resp.Data[0].([]interface{})[0].(string) != "tt" { t.Fatalf("result is not {{1}} : %v", resp.Data) } } diff --git a/multi/call_17_test.go b/multi/call_17_test.go index 86a3cfc13..378961fda 100644 --- a/multi/call_17_test.go +++ b/multi/call_17_test.go @@ -23,11 +23,11 @@ func TestCall(t *testing.T) { defer multiConn.Close() // Call17 - resp, err = multiConn.Call("simple_incr", []interface{}{1}) + resp, err = multiConn.Call("simple_concat", []interface{}{"t"}) if err != nil { t.Fatalf("Failed to use Call: %s", err.Error()) } - if resp.Data[0].(uint64) != 2 { + if resp.Data[0].(string) != "tt" { t.Fatalf("result is not {{1}} : %v", resp.Data) } } diff --git a/multi/config.lua b/multi/config.lua index 0f032b204..aca4db9b3 100644 --- a/multi/config.lua +++ b/multi/config.lua @@ -16,7 +16,7 @@ box.once("init", function() id = 517, if_not_exists = true, }) - s:create_index('primary', {type = 'tree', parts = {1, 'uint'}, if_not_exists = true}) + s:create_index('primary', {type = 'tree', parts = {1, 'string'}, if_not_exists = true}) box.schema.user.create('test', { password = 'test' }) box.schema.user.grant('test', 'read,write,execute', 'universe') @@ -37,11 +37,11 @@ box.once("init", function() box.schema.user.grant('test', 'create', 'sequence') end) -local function simple_incr(a) - return a + 1 +local function simple_concat(a) + return a .. a end -rawset(_G, 'simple_incr', simple_incr) +rawset(_G, 'simple_concat', simple_concat) -- Set listen only when every other thing is configured. box.cfg{ diff --git a/multi/multi_test.go b/multi/multi_test.go index b4cdf18af..d95a51f03 100644 --- a/multi/multi_test.go +++ b/multi/multi_test.go @@ -229,11 +229,11 @@ func TestCall17(t *testing.T) { defer multiConn.Close() // Call17 - resp, err = multiConn.Call17("simple_incr", []interface{}{1}) + resp, err = multiConn.Call17("simple_concat", []interface{}{"s"}) if err != nil { t.Fatalf("Failed to use Call: %s", err.Error()) } - if resp.Data[0].(uint64) != 2 { + if resp.Data[0].(string) != "ss" { t.Fatalf("result is not {{1}} : %v", resp.Data) } } @@ -351,7 +351,7 @@ func TestStream_Commit(t *testing.T) { // Insert in stream req = tarantool.NewInsertRequest(spaceName). - Tuple([]interface{}{uint(1001), "hello2", "world2"}) + Tuple([]interface{}{"1001", "hello2", "world2"}) resp, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) @@ -359,7 +359,7 @@ func TestStream_Commit(t *testing.T) { if resp.Code != tarantool.OkCode { t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) } - defer test_helpers.DeleteRecordByKey(t, multiConn, spaceNo, indexNo, []interface{}{uint(1001)}) + defer test_helpers.DeleteRecordByKey(t, multiConn, spaceNo, indexNo, []interface{}{"1001"}) // Select not related to the transaction // while transaction is not committed @@ -368,7 +368,7 @@ func TestStream_Commit(t *testing.T) { Index(indexNo). Limit(1). Iterator(tarantool.IterEq). - Key([]interface{}{uint(1001)}) + Key([]interface{}{"1001"}) resp, err = multiConn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) @@ -394,7 +394,7 @@ func TestStream_Commit(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, ok := tpl[0].(string); !ok || id != "1001" { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { @@ -429,7 +429,7 @@ func TestStream_Commit(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, ok := tpl[0].(string); !ok || id != "1001" { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { @@ -471,7 +471,7 @@ func TestStream_Rollback(t *testing.T) { // Insert in stream req = tarantool.NewInsertRequest(spaceName). - Tuple([]interface{}{uint(1001), "hello2", "world2"}) + Tuple([]interface{}{"1001", "hello2", "world2"}) resp, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) @@ -479,7 +479,7 @@ func TestStream_Rollback(t *testing.T) { if resp.Code != tarantool.OkCode { t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) } - defer test_helpers.DeleteRecordByKey(t, multiConn, spaceNo, indexNo, []interface{}{uint(1001)}) + defer test_helpers.DeleteRecordByKey(t, multiConn, spaceNo, indexNo, []interface{}{"1001"}) // Select not related to the transaction // while transaction is not committed @@ -488,7 +488,7 @@ func TestStream_Rollback(t *testing.T) { Index(indexNo). Limit(1). Iterator(tarantool.IterEq). - Key([]interface{}{uint(1001)}) + Key([]interface{}{"1001"}) resp, err = multiConn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) @@ -514,7 +514,7 @@ func TestStream_Rollback(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, ok := tpl[0].(string); !ok || id != "1001" { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { diff --git a/queue/msgpack.go b/queue/msgpack.go index d614374ce..d9e0b58db 100644 --- a/queue/msgpack.go +++ b/queue/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package queue import ( diff --git a/queue/msgpack_helper_test.go b/queue/msgpack_helper_test.go index cfb8d8e89..49b61240c 100644 --- a/queue/msgpack_helper_test.go +++ b/queue/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package queue_test import ( diff --git a/queue/msgpack_v5.go b/queue/msgpack_v5.go new file mode 100644 index 000000000..b5037caaf --- /dev/null +++ b/queue/msgpack_v5.go @@ -0,0 +1,10 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package queue + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type decoder = msgpack.Decoder diff --git a/queue/msgpack_v5_helper_test.go b/queue/msgpack_v5_helper_test.go new file mode 100644 index 000000000..ea2991f34 --- /dev/null +++ b/queue/msgpack_v5_helper_test.go @@ -0,0 +1,11 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package queue_test + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder diff --git a/queue/queue.go b/queue/queue.go index 4fe8fe296..ef40154b0 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -283,14 +283,27 @@ func (q *queue) produce(cmd string, params ...interface{}) (string, error) { return qd.task.status, nil } +type kickResult struct { + id uint64 +} + +func (r *kickResult) DecodeMsgpack(d *decoder) (err error) { + var l int + if l, err = d.DecodeArrayLen(); err != nil { + return err + } + if l > 1 { + return fmt.Errorf("array len doesn't match for queue kick data: %d", l) + } + r.id, err = d.DecodeUint64() + return +} + // Reverse the effect of a bury request on one or more tasks. func (q *queue) Kick(count uint64) (uint64, error) { - resp, err := q.conn.Call17(q.cmds.kick, []interface{}{count}) - var id uint64 - if err == nil { - id = resp.Data[0].(uint64) - } - return id, err + var r kickResult + err := q.conn.Call17Typed(q.cmds.kick, []interface{}{count}, &r) + return r.id, err } // Delete the task identified by its id. diff --git a/tarantool_test.go b/tarantool_test.go index 18ebb0506..c1d49f12e 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -56,6 +56,36 @@ func (m *Member) DecodeMsgpack(d *decoder) error { return nil } +// msgpack.v2 and msgpack.v5 return different uint types in responses. The +// function helps to unify a result. +func convertUint64(v interface{}) (result uint64, err error) { + switch v := v.(type) { + case uint: + result = uint64(v) + case uint8: + result = uint64(v) + case uint16: + result = uint64(v) + case uint32: + result = uint64(v) + case uint64: + result = uint64(v) + case int: + result = uint64(v) + case int8: + result = uint64(v) + case int16: + result = uint64(v) + case int32: + result = uint64(v) + case int64: + result = uint64(v) + default: + err = fmt.Errorf("Non-number value %T", v) + } + return +} + var server = "127.0.0.1:3013" var spaceNo = uint32(517) var spaceName = "test" @@ -726,7 +756,7 @@ func TestClient(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Insert (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != 1 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1 { t.Errorf("Unexpected body of Insert (0)") } if h, ok := tpl[1].(string); !ok || h != "hello" { @@ -759,7 +789,7 @@ func TestClient(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Delete (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != 1 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1 { t.Errorf("Unexpected body of Delete (0)") } if h, ok := tpl[1].(string); !ok || h != "hello" { @@ -801,7 +831,7 @@ func TestClient(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Replace (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != 2 { + if id, err := convertUint64(tpl[0]); err != nil || id != 2 { t.Errorf("Unexpected body of Replace (0)") } if h, ok := tpl[1].(string); !ok || h != "hi" { @@ -826,7 +856,7 @@ func TestClient(t *testing.T) { if len(tpl) != 2 { t.Errorf("Unexpected body of Update (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != 2 { + if id, err := convertUint64(tpl[0]); err != nil || id != 2 { t.Errorf("Unexpected body of Update (0)") } if h, ok := tpl[1].(string); !ok || h != "bye" { @@ -875,7 +905,7 @@ func TestClient(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 10 { + if id, err := convertUint64(tpl[0]); err != nil || id != 10 { t.Errorf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "val 10" { @@ -966,19 +996,19 @@ func TestClient(t *testing.T) { } // Call16 vs Call17 - resp, err = conn.Call16("simple_incr", []interface{}{1}) + resp, err = conn.Call16("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call16") } - if resp.Data[0].([]interface{})[0].(uint64) != 2 { + if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } - resp, err = conn.Call17("simple_incr", []interface{}{1}) + resp, err = conn.Call17("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].(uint64) != 2 { + if val, ok := resp.Data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } @@ -993,8 +1023,7 @@ func TestClient(t *testing.T) { if len(resp.Data) < 1 { t.Errorf("Response.Data is empty after Eval") } - val := resp.Data[0].(uint64) - if val != 11 { + if val, err := convertUint64(resp.Data[0]); err != nil || val != 11 { t.Errorf("5 + 6 == 11, but got %v", val) } } @@ -1039,7 +1068,7 @@ func TestClientSessionPush(t *testing.T) { t.Errorf("Response is nil after CallAsync") } else if len(resp.Data) < 1 { t.Errorf("Response.Data is empty after Call17Async") - } else if resp.Data[0].(uint64) != pushMax { + } else if val, err := convertUint64(resp.Data[0]); err != nil || val != pushMax { t.Errorf("result is not {{1}} : %v", resp.Data) } @@ -1068,12 +1097,12 @@ func TestClientSessionPush(t *testing.T) { } if resp.Code == PushCode { pushCnt += 1 - if resp.Data[0].(uint64) != pushCnt { + if val, err := convertUint64(resp.Data[0]); err != nil || val != pushCnt { t.Errorf("Unexpected push data = %v", resp.Data) } } else { respCnt += 1 - if resp.Data[0].(uint64) != pushMax { + if val, err := convertUint64(resp.Data[0]); err != nil || val != pushMax { t.Errorf("result is not {{1}} : %v", resp.Data) } } @@ -1094,13 +1123,13 @@ func TestClientSessionPush(t *testing.T) { } const ( - createTableQuery = "CREATE TABLE SQL_SPACE (id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING COLLATE \"unicode\" DEFAULT NULL);" + createTableQuery = "CREATE TABLE SQL_SPACE (id STRING PRIMARY KEY, name STRING COLLATE \"unicode\" DEFAULT NULL);" insertQuery = "INSERT INTO SQL_SPACE VALUES (?, ?);" selectNamedQuery = "SELECT id, name FROM SQL_SPACE WHERE id=:id AND name=:name;" selectPosQuery = "SELECT id, name FROM SQL_SPACE WHERE id=? AND name=?;" updateQuery = "UPDATE SQL_SPACE SET name=? WHERE id=?;" enableFullMetaDataQuery = "SET SESSION \"sql_full_metadata\" = true;" - selectSpanDifQuery = "SELECT id*2, name, id FROM SQL_SPACE WHERE name=?;" + selectSpanDifQuery = "SELECT id||id, name, id FROM SQL_SPACE WHERE name=?;" alterTableQuery = "ALTER TABLE SQL_SPACE RENAME TO SQL_SPACE2;" insertIncrQuery = "INSERT INTO SQL_SPACE2 VALUES (?, ?);" deleteQuery = "DELETE FROM SQL_SPACE2 WHERE name=?;" @@ -1135,7 +1164,7 @@ func TestSQL(t *testing.T) { }, { insertQuery, - []interface{}{1, "test"}, + []interface{}{"1", "test"}, Response{ SQLInfo: SQLInfo{AffectedCount: 1}, Data: []interface{}{}, @@ -1145,31 +1174,31 @@ func TestSQL(t *testing.T) { { selectNamedQuery, map[string]interface{}{ - "id": 1, + "id": "1", "name": "test", }, Response{ SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{[]interface{}{uint64(1), "test"}}, + Data: []interface{}{[]interface{}{"1", "test"}}, MetaData: []ColumnMetaData{ - {FieldType: "integer", FieldName: "ID"}, + {FieldType: "string", FieldName: "ID"}, {FieldType: "string", FieldName: "NAME"}}, }, }, { selectPosQuery, - []interface{}{1, "test"}, + []interface{}{"1", "test"}, Response{ SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{[]interface{}{uint64(1), "test"}}, + Data: []interface{}{[]interface{}{"1", "test"}}, MetaData: []ColumnMetaData{ - {FieldType: "integer", FieldName: "ID"}, + {FieldType: "string", FieldName: "ID"}, {FieldType: "string", FieldName: "NAME"}}, }, }, { updateQuery, - []interface{}{"test_test", 1}, + []interface{}{"test_test", "1"}, Response{ SQLInfo: SQLInfo{AffectedCount: 1}, Data: []interface{}{}, @@ -1189,14 +1218,14 @@ func TestSQL(t *testing.T) { selectSpanDifQuery, []interface{}{"test_test"}, Response{ - SQLInfo: SQLInfo{AffectedCount: 0}, Data: []interface{}{[]interface{}{uint64(2), "test_test", uint64(1)}}, + SQLInfo: SQLInfo{AffectedCount: 0}, Data: []interface{}{[]interface{}{"11", "test_test", "1"}}, MetaData: []ColumnMetaData{ { - FieldType: "integer", + FieldType: "string", FieldName: "COLUMN_1", FieldIsNullable: false, FieldIsAutoincrement: false, - FieldSpan: "id*2", + FieldSpan: "id||id", }, { FieldType: "string", @@ -1207,11 +1236,12 @@ func TestSQL(t *testing.T) { FieldCollation: "unicode", }, { - FieldType: "integer", + FieldType: "string", FieldName: "ID", FieldIsNullable: false, - FieldIsAutoincrement: true, + FieldIsAutoincrement: false, FieldSpan: "id", + FieldCollation: "", }, }}, }, @@ -1226,7 +1256,7 @@ func TestSQL(t *testing.T) { }, { insertIncrQuery, - []interface{}{2, "test_2"}, + []interface{}{"2", "test_2"}, Response{ SQLInfo: SQLInfo{AffectedCount: 1, InfoAutoincrementIds: []uint64{1}}, Data: []interface{}{}, @@ -1944,7 +1974,7 @@ func TestClientRequestObjects(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Insert (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != uint64(i) { + if id, err := convertUint64(tpl[0]); err != nil || id != uint64(i) { t.Errorf("Unexpected body of Insert (0)") } if h, ok := tpl[1].(string); !ok || h != fmt.Sprintf("val %d", i) { @@ -1979,7 +2009,7 @@ func TestClientRequestObjects(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Replace (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != uint64(i) { + if id, err := convertUint64(tpl[0]); err != nil || id != uint64(i) { t.Errorf("Unexpected body of Replace (0)") } if h, ok := tpl[1].(string); !ok || h != fmt.Sprintf("val %d", i) { @@ -2013,7 +2043,7 @@ func TestClientRequestObjects(t *testing.T) { if len(tpl) != 3 { t.Errorf("Unexpected body of Delete (tuple len)") } - if id, ok := tpl[0].(uint64); !ok || id != uint64(1016) { + if id, err := convertUint64(tpl[0]); err != nil || id != uint64(1016) { t.Errorf("Unexpected body of Delete (0)") } if h, ok := tpl[1].(string); !ok || h != "val 1016" { @@ -2044,7 +2074,7 @@ func TestClientRequestObjects(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Update") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1010 { + if id, err := convertUint64(tpl[0]); err != nil || id != uint64(1010) { t.Errorf("Unexpected body of Update (0)") } if h, ok := tpl[1].(string); !ok || h != "val 1010" { @@ -2076,13 +2106,13 @@ func TestClientRequestObjects(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1010 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1010 { t.Errorf("Unexpected body of Update (0)") } if h, ok := tpl[1].(string); !ok || h != "bye" { t.Errorf("Unexpected body of Update (1)") } - if h, ok := tpl[2].(uint64); !ok || h != 1 { + if h, err := convertUint64(tpl[2]); err != nil || h != 1 { t.Errorf("Unexpected body of Update (2)") } } @@ -2142,8 +2172,8 @@ func TestClientRequestObjects(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1010 { - t.Errorf("Unexpected body of Select (0) %d, expected %d", tpl[0].(uint64), 1010) + if id, err := convertUint64(tpl[0]); err != nil || id != 1010 { + t.Errorf("Unexpected body of Select (0) %v, expected %d", tpl[0], 1010) } if h, ok := tpl[1].(string); !ok || h != "bye" { t.Errorf("Unexpected body of Select (1) %q, expected %q", tpl[1].(string), "bye") @@ -2154,22 +2184,22 @@ func TestClientRequestObjects(t *testing.T) { } // Call16 vs Call17 - req = NewCall16Request("simple_incr").Args([]interface{}{1}) + req = NewCall16Request("simple_concat").Args([]interface{}{"1"}) resp, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call") } - if resp.Data[0].([]interface{})[0].(uint64) != 2 { + if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } // Call17 - req = NewCall17Request("simple_incr").Args([]interface{}{1}) + req = NewCall17Request("simple_concat").Args([]interface{}{"1"}) resp, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call17") } - if resp.Data[0].(uint64) != 2 { + if val, ok := resp.Data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", resp.Data) } @@ -2185,8 +2215,7 @@ func TestClientRequestObjects(t *testing.T) { if len(resp.Data) < 1 { t.Errorf("Response.Data is empty after Eval") } - val := resp.Data[0].(uint64) - if val != 11 { + if val, err := convertUint64(resp.Data[0]); err != nil || val != 11 { t.Errorf("5 + 6 == 11, but got %v", val) } @@ -2386,7 +2415,7 @@ func TestStream_Commit(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1001 { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { @@ -2421,7 +2450,7 @@ func TestStream_Commit(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1001 { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { @@ -2501,7 +2530,7 @@ func TestStream_Rollback(t *testing.T) { if tpl, ok := resp.Data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { - if id, ok := tpl[0].(uint64); !ok || id != 1001 { + if id, err := convertUint64(tpl[0]); err != nil || id != 1001 { t.Fatalf("Unexpected body of Select (0)") } if h, ok := tpl[1].(string); !ok || h != "hello2" { @@ -2606,8 +2635,8 @@ func TestStream_TxnIsolationLevel(t *testing.T) { require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 3, len(tpl), "unexpected body of Select") - key, ok := tpl[0].(uint64) - require.Truef(t, ok, "unexpected body of Select (0)") + key, err := convertUint64(tpl[0]) + require.Nilf(t, err, "unexpected body of Select (0)") require.Equalf(t, uint64(1001), key, "unexpected body of Select (0)") value1, ok := tpl[1].(string) diff --git a/test_helpers/msgpack.go b/test_helpers/msgpack.go index f18b17f48..1ea712b38 100644 --- a/test_helpers/msgpack.go +++ b/test_helpers/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package test_helpers import ( diff --git a/test_helpers/msgpack_v5.go b/test_helpers/msgpack_v5.go new file mode 100644 index 000000000..37f85ef31 --- /dev/null +++ b/test_helpers/msgpack_v5.go @@ -0,0 +1,10 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package test_helpers + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder diff --git a/uuid/msgpack.go b/uuid/msgpack.go index 1fcbc711b..62504e5f9 100644 --- a/uuid/msgpack.go +++ b/uuid/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package uuid import ( diff --git a/uuid/msgpack_helper_test.go b/uuid/msgpack_helper_test.go index e8550f050..d5a1cb70e 100644 --- a/uuid/msgpack_helper_test.go +++ b/uuid/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package uuid_test import ( diff --git a/uuid/msgpack_v5.go b/uuid/msgpack_v5.go new file mode 100644 index 000000000..951c437dd --- /dev/null +++ b/uuid/msgpack_v5.go @@ -0,0 +1,27 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package uuid + +import ( + "reflect" + + "github.com/google/uuid" + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder + +func init() { + msgpack.Register(reflect.TypeOf((*uuid.UUID)(nil)).Elem(), encodeUUID, decodeUUID) + msgpack.RegisterExtEncoder(UUID_extId, uuid.UUID{}, + func(e *msgpack.Encoder, v reflect.Value) ([]byte, error) { + uuid := v.Interface().(uuid.UUID) + return uuid.MarshalBinary() + }) + msgpack.RegisterExtDecoder(UUID_extId, uuid.UUID{}, + func(d *msgpack.Decoder, v reflect.Value, extLen int) error { + return decodeUUID(d, v) + }) +} diff --git a/uuid/msgpack_v5_helper_test.go b/uuid/msgpack_v5_helper_test.go new file mode 100644 index 000000000..c2356ef1a --- /dev/null +++ b/uuid/msgpack_v5_helper_test.go @@ -0,0 +1,10 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package uuid_test + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type decoder = msgpack.Decoder From 67653e9afa31534a71344f6fccffba85035566c9 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Thu, 19 May 2022 11:32:23 +0300 Subject: [PATCH 7/7] github-ci: add tests for msgpack.v5 Closes #124 --- .github/workflows/testing.yml | 26 +++++++++++++++++++++++--- CONTRIBUTING.md | 5 +++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 978073afd..eee435bef 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -71,9 +71,15 @@ jobs: - name: Run regression tests run: make test - - name: Run tests with call_17 + - name: Run regression tests with call_17 run: make test TAGS="go_tarantool_call_17" + - name: Run regression tests with msgpack.v5 + run: make test TAGS="go_tarantool_msgpack_v5" + + - name: Run regression tests with msgpack.v5 and call_17 + run: make test TAGS="go_tarantool_msgpack_v5,go_tarantool_call_17" + - name: Run fuzzing tests if: ${{ matrix.fuzzing }} run: make fuzzing TAGS="go_tarantool_decimal_fuzzing" @@ -146,20 +152,34 @@ jobs: source tarantool-enterprise/env.sh make deps - - name: Run tests + - name: Run regression tests run: | source tarantool-enterprise/env.sh make test env: TEST_TNT_SSL: ${{matrix.ssl}} - - name: Run tests with call_17 + - name: Run regression tests with call_17 run: | source tarantool-enterprise/env.sh make test TAGS="go_tarantool_call_17" env: TEST_TNT_SSL: ${{matrix.ssl}} + - name: Run regression tests with msgpack.v5 + run: | + source tarantool-enterprise/env.sh + make test TAGS="go_tarantool_msgpack_v5" + env: + TEST_TNT_SSL: ${{matrix.ssl}} + + - name: Run regression tests with msgpack.v5 and call_17 + run: | + source tarantool-enterprise/env.sh + make test TAGS="go_tarantool_msgpack_v5,go_tarantool_call_17" + env: + TEST_TNT_SSL: ${{matrix.ssl}} + - name: Run fuzzing tests if: ${{ matrix.fuzzing }} run: make fuzzing TAGS="go_tarantool_decimal_fuzzing" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8d493e40..15bccd3a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,11 @@ make test The tests set up all required `tarantool` processes before run and clean up afterwards. +If you want to run the tests with specific build tags: +```bash +make test TAGS=go_tarantool_ssl_disable,go_tarantool_msgpack_v5 +``` + If you have Tarantool Enterprise Edition 2.10 or newer, you can run additional SSL tests. To do this, you need to set an environment variable 'TEST_TNT_SSL':