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/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/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': 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/client_tools.go b/client_tools.go index 27976a9a7..a5d6fbba9 100644 --- a/client_tools.go +++ b/client_tools.go @@ -1,18 +1,14 @@ 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 { - enc.EncodeSliceLen(1) - enc.EncodeInt(k.I) +func (k IntKey) EncodeMsgpack(enc *encoder) error { + enc.EncodeArrayLen(1) + encodeInt(enc, int64(k.I)) return nil } @@ -22,9 +18,9 @@ type UintKey struct { I uint } -func (k UintKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(1) - enc.EncodeUint(k.I) +func (k UintKey) EncodeMsgpack(enc *encoder) error { + enc.EncodeArrayLen(1) + encodeUint(enc, uint64(k.I)) return nil } @@ -34,8 +30,8 @@ type StringKey struct { S string } -func (k StringKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(1) +func (k StringKey) EncodeMsgpack(enc *encoder) error { + enc.EncodeArrayLen(1) enc.EncodeString(k.S) return nil } @@ -46,10 +42,10 @@ type IntIntKey struct { I1, I2 int } -func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(2) - enc.EncodeInt(k.I1) - enc.EncodeInt(k.I2) +func (k IntIntKey) EncodeMsgpack(enc *encoder) error { + enc.EncodeArrayLen(2) + encodeInt(enc, int64(k.I1)) + encodeInt(enc, int64(k.I2)) return nil } @@ -60,10 +56,10 @@ type Op struct { Arg interface{} } -func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(3) +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) } @@ -148,12 +144,12 @@ type OpSplice struct { Replace string } -func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error { - enc.EncodeSliceLen(5) +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/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/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 93d292ecd..7d9dbe29a 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{ @@ -65,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) } } @@ -270,8 +269,8 @@ type Tuple1 struct { Datetime Datetime } -func (t *Tuple1) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { +func (t *Tuple1) EncodeMsgpack(e *encoder) error { + if err := e.EncodeArrayLen(2); err != nil { return err } if err := e.Encode(&t.Datetime); err != nil { @@ -280,10 +279,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { @@ -296,8 +295,8 @@ func (t *Tuple1) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (ev *Event) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { +func (ev *Event) EncodeMsgpack(e *encoder) error { + if err := e.EncodeArrayLen(2); err != nil { return err } if err := e.EncodeString(ev.Location); err != nil { @@ -309,10 +308,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 2 { @@ -325,15 +324,18 @@ func (ev *Event) DecodeMsgpack(d *msgpack.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 } -func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(3); err != nil { +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 := e.EncodeUint64(uint64(c.Cid)); err != nil { return err } if err := e.EncodeString(c.Orig); err != nil { @@ -343,10 +345,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 3 { @@ -358,7 +360,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) @@ -429,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) } } @@ -502,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") } } @@ -525,7 +527,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 +551,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..4f48f1d3e --- /dev/null +++ b/datetime/msgpack.go @@ -0,0 +1,12 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +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..7af2ee6ad --- /dev/null +++ b/datetime/msgpack_helper_test.go @@ -0,0 +1,26 @@ +//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 toDatetime(i interface{}) (dt Datetime, ok bool) { + dt, ok = i.(Datetime) + 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/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.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 1c395b393..6fba0cc8f 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,17 +39,17 @@ type TupleDecimal struct { number Decimal } -func (t *TupleDecimal) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(1); err != nil { +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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { @@ -61,8 +60,10 @@ func (t *TupleDecimal) DecodeMsgpack(d *msgpack.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 } @@ -144,11 +145,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 +248,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 +262,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 +281,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 +311,7 @@ func BenchmarkMPEncodeDecimal(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - msgpack.Marshal(decNum) + marshal(decNum) } }) } @@ -324,13 +325,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) } }) @@ -348,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)") } } @@ -417,7 +418,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,11 +443,14 @@ 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()) } - 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 new file mode 100644 index 000000000..5a455ae59 --- /dev/null +++ b/decimal/msgpack.go @@ -0,0 +1,12 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +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..3824f70b8 --- /dev/null +++ b/decimal/msgpack_helper_test.go @@ -0,0 +1,26 @@ +//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) +} + +func unmarshal(data []byte, v interface{}) error { + return msgpack.Unmarshal(data, 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_custom_unpacking_test.go b/example_custom_unpacking_test.go index 2910010cd..4087c5620 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,11 +23,11 @@ type Tuple3 struct { Members []Member } -func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(3); err != nil { +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 { @@ -38,10 +37,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 3 { @@ -53,7 +52,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/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/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/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 new file mode 100644 index 000000000..34ecc4b3b --- /dev/null +++ b/msgpack.go @@ -0,0 +1,50 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +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 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 || + 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..fa47c2fda --- /dev/null +++ b/msgpack_helper_test.go @@ -0,0 +1,15 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package tarantool_test + +import ( + "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)) +} 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/prepared.go b/prepared.go index 0f9303344..6f63e3bb1 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,23 +18,23 @@ type Prepared struct { Conn *Connection } -func fillPrepare(enc *msgpack.Encoder, expr string) error { +func fillPrepare(enc *encoder, expr string) error { enc.EncodeMapLen(1) - enc.EncodeUint64(KeySQLText) + encodeUint(enc, KeySQLText) return enc.EncodeString(expr) } -func fillUnprepare(enc *msgpack.Encoder, stmt Prepared) error { +func fillUnprepare(enc *encoder, stmt Prepared) error { enc.EncodeMapLen(1) - enc.EncodeUint64(KeyStmtID) - return enc.EncodeUint64(uint64(stmt.StatementID)) + encodeUint(enc, KeyStmtID) + return encodeUint(enc, uint64(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.EncodeUint64(KeyStmtID) - enc.EncodeUint64(uint64(stmt.StatementID)) - enc.EncodeUint64(KeySQLBind) + encodeUint(enc, KeyStmtID) + encodeUint(enc, uint64(stmt.StatementID)) + encodeUint(enc, KeySQLBind) return encodeSQLBind(enc, args) } @@ -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..d9e0b58db --- /dev/null +++ b/queue/msgpack.go @@ -0,0 +1,10 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +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..49b61240c --- /dev/null +++ b/queue/msgpack_helper_test.go @@ -0,0 +1,11 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package queue_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder +type decoder = msgpack.Decoder 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 8a3e3e17d..ef40154b0 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. @@ -284,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. @@ -336,10 +348,10 @@ 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.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..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,10 +182,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 { @@ -198,8 +197,8 @@ func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (c *customData) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(1); err != nil { +func (c *customData) EncodeMsgpack(e *encoder) error { + 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..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,10 +12,10 @@ 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.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..cfa40e522 100644 --- a/request.go +++ b/request.go @@ -6,93 +6,91 @@ import ( "reflect" "strings" "sync" - - "gopkg.in/vmihailenco/msgpack.v2" ) -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) +func fillSearch(enc *encoder, spaceNo, indexNo uint32, key interface{}) error { + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyIndexNo) + encodeUint(enc, uint64(indexNo)) + encodeUint(enc, 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)) +func fillIterator(enc *encoder, offset, limit, iterator uint32) { + encodeUint(enc, KeyIterator) + encodeUint(enc, uint64(iterator)) + encodeUint(enc, KeyOffset) + encodeUint(enc, uint64(offset)) + encodeUint(enc, KeyLimit) + encodeUint(enc, uint64(limit)) } -func fillInsert(enc *msgpack.Encoder, spaceNo uint32, tuple interface{}) error { +func fillInsert(enc *encoder, spaceNo uint32, tuple interface{}) error { enc.EncodeMapLen(2) - enc.EncodeUint64(KeySpaceNo) - enc.EncodeUint64(uint64(spaceNo)) - enc.EncodeUint64(KeyTuple) + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyTuple) 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 } - enc.EncodeUint64(KeyTuple) + encodeUint(enc, KeyTuple) 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.EncodeUint64(KeySpaceNo) - enc.EncodeUint64(uint64(spaceNo)) - enc.EncodeUint64(KeyTuple) + encodeUint(enc, KeySpaceNo) + encodeUint(enc, uint64(spaceNo)) + encodeUint(enc, KeyTuple) if err := enc.Encode(tuple); err != nil { return err } - enc.EncodeUint64(KeyDefTuple) + encodeUint(enc, KeyDefTuple) 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.EncodeUint64(KeyFunctionName) + encodeUint(enc, KeyFunctionName) enc.EncodeString(functionName) - enc.EncodeUint64(KeyTuple) + encodeUint(enc, KeyTuple) 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.EncodeUint64(KeyExpression) + encodeUint(enc, KeyExpression) enc.EncodeString(expr) - enc.EncodeUint64(KeyTuple) + encodeUint(enc, KeyTuple) 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.EncodeUint64(KeySQLText) + encodeUint(enc, KeySQLText) enc.EncodeString(expr) - enc.EncodeUint64(KeySQLBind) + encodeUint(enc, KeySQLBind) return encodeSQLBind(enc, args) } -func fillPing(enc *msgpack.Encoder) error { +func fillPing(enc *encoder) error { return enc.EncodeMapLen(0) } @@ -197,10 +195,10 @@ 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.DecodeSliceLen(); err != nil { + if len, err = d.DecodeArrayLen(); err != nil { return err } if s.found = len >= 1; !s.found { @@ -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 { @@ -433,7 +431,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 +443,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 +477,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 +488,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++ { @@ -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 b1e8562d2..ecf565762 100644 --- a/schema.go +++ b/schema.go @@ -1,9 +1,21 @@ package tarantool import ( + "errors" "fmt" ) +//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. type SchemaResolver interface { // ResolveSpaceIndex returns resolved space and index numbers or an @@ -37,19 +49,198 @@ type Space struct { IndexesById map[uint32]*Index } +func (space *Space) DecodeMsgpack(d *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 msgpackIsString(code) { + val, err := d.DecodeString() + if err != nil { + return err + } + space.Temporary = val == "temporary" + } else if msgpackIsMap(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 *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 *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 msgpackIsUint(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 msgpackIsUint(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 +248,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 *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 msgpackIsMap(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 msgpackIsArray(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 } diff --git a/stream.go b/stream.go index fdfe8408c..3a03ec68f 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 @@ -46,7 +44,7 @@ func fillBegin(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout tim } if hasTimeout { - err = enc.EncodeUint64(KeyTimeout) + err = encodeUint(enc, KeyTimeout) if err != nil { return err } @@ -58,12 +56,12 @@ func fillBegin(enc *msgpack.Encoder, txnIsolation TxnIsolationLevel, timeout tim } 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 } @@ -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 de4062c9e..c1d49f12e 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,23 +25,23 @@ type Member struct { Val uint } -func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { - if err := e.EncodeSliceLen(2); err != nil { +func (m *Member) EncodeMsgpack(e *encoder) error { + if err := e.EncodeArrayLen(2); err != nil { return err } 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 } -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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 2 { @@ -57,6 +56,36 @@ func (m *Member) DecodeMsgpack(d *msgpack.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" @@ -727,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" { @@ -760,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" { @@ -802,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" { @@ -827,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" { @@ -876,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" { @@ -967,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) } @@ -994,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) } } @@ -1040,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) } @@ -1069,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) } } @@ -1095,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=?;" @@ -1136,7 +1164,7 @@ func TestSQL(t *testing.T) { }, { insertQuery, - []interface{}{1, "test"}, + []interface{}{"1", "test"}, Response{ SQLInfo: SQLInfo{AffectedCount: 1}, Data: []interface{}{}, @@ -1146,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{}{}, @@ -1190,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", @@ -1208,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: "", }, }}, }, @@ -1227,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{}{}, @@ -1945,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) { @@ -1980,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) { @@ -2014,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" { @@ -2045,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" { @@ -2077,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)") } } @@ -2143,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") @@ -2155,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) } @@ -2186,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) } @@ -2387,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" { @@ -2422,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" { @@ -2502,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" { @@ -2607,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 new file mode 100644 index 000000000..1ea712b38 --- /dev/null +++ b/test_helpers/msgpack.go @@ -0,0 +1,10 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package test_helpers + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder 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/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..62504e5f9 --- /dev/null +++ b/uuid/msgpack.go @@ -0,0 +1,19 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +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..d5a1cb70e --- /dev/null +++ b/uuid/msgpack_helper_test.go @@ -0,0 +1,10 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package uuid_test + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type decoder = msgpack.Decoder 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 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 554dadbe1..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,10 +32,10 @@ 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.DecodeSliceLen(); err != nil { + if l, err = d.DecodeArrayLen(); err != nil { return err } if l != 1 {