Skip to content

Commit 6fafc17

Browse files
committed
msgpack: add optional msgpack.v5 support
The msgpack.v5 code located in msgpack_v5.go and msgpack_v5_helper_test.go for tests. It is the same logic for submodules. An user can use msgpack.v5 with a build tag: go_tarantool_msgpack_v5 The commit also unify typo conversions after decoding in tests. This is necessary because msgpack v2.9.2 decodes all numbers as uint[1] or int[2], but msgpack.v5 decodes numbers as a MessagePack type[3]. There are additional differences when decoding types. 1. https://github.com/vmihailenco/msgpack/blob/23644a15054d8b9f8a9fca3e041f3419504b9c21/decode.go#L283 2. https://github.com/vmihailenco/msgpack/blob/23644a15054d8b9f8a9fca3e041f3419504b9c21/decode.go#L285 3. https://github.com/vmihailenco/msgpack/blob/233c977ae92b215a9aa7eb420bbe67da99f05ab6/decode.go#L410 Part of #124
1 parent 5bcf130 commit 6fafc17

38 files changed

+460
-109
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1818
- Prepared SQL statements (#117)
1919
- Context support for request objects (#48)
2020
- Streams and interactive transactions support (#101)
21+
- Optional msgpack.v5 usage (#124)
2122

2223
### Changed
2324

README.md

+35-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ faster than other packages according to public benchmarks.
2626
* [Documentation](#documentation)
2727
* [API reference](#api-reference)
2828
* [Walking\-through example](#walking-through-example)
29+
* [msgpack.v5 migration](#msgpackv5-migration)
2930
* [Contributing](#contributing)
3031
* [Alternative connectors](#alternative-connectors)
3132

@@ -68,7 +69,15 @@ This allows us to introduce new features without losing backward compatibility.
6869
go_tarantool_call_17
6970
```
7071
**Note:** In future releases, `Call17` may be used as default `Call` behavior.
71-
3. To run fuzz tests with decimals, you can use the build tag:
72+
3. To replace usage of `msgpack.v2` with `msgpack.v5`, you can use the build
73+
tag:
74+
```
75+
go_tarantool_msgpack_v5
76+
```
77+
**Note:** In future releases, `msgpack.v5` may be used by default. We recommend
78+
to read [msgpack.v5 migration notes](#msgpackv5-migration) and try to
79+
use msgpack.v5 before the changes.
80+
4. To run fuzz tests with decimals, you can use the build tag:
7281
```
7382
go_tarantool_decimal_fuzzing
7483
```
@@ -144,6 +153,31 @@ There are two parameters:
144153
* a space number (it could just as easily have been a space name), and
145154
* a tuple.
146155

156+
### msgpack.v5 migration
157+
158+
Most function names and argument types in `msgpack.v5` and `msgpack.v2`
159+
have not changed (in our code, we noticed changes in `EncodeInt`, `EncodeUint`
160+
and `RegisterExt`). But there are a lot of changes in a logic of encoding and
161+
decoding. On the plus side the migration seems easy, but on the minus side you
162+
need to be very careful.
163+
164+
First of all, `EncodeInt8`, `EncodeInt16`, `EncodeInt32`, `EncodeInt64`
165+
and `EncodeUint*` analogues at `msgpack.v5` encode numbers as is without loss of
166+
type. In `msgpack.v2` the type of a number is reduced to a value.
167+
168+
Secondly, a base decoding function does not convert numbers to `int64` or
169+
`uint64`. It converts numbers to an exact type defined by MessagePack. The
170+
change makes manual type conversions much more difficult and can lead to
171+
runtime errors with an old code. We do not recommend to use type conversions
172+
and give preference to `*Typed` functions (besides, it's faster).
173+
174+
There are also changes in the logic that can lead to errors in the old code,
175+
[as example](https://github.com/vmihailenco/msgpack/issues/327). Although in
176+
`msgpack.v5` some functions for the logic tuning were added (see
177+
[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
178+
to achieve full compliance of behavior between `msgpack.v5` and `msgpack.v2`. So
179+
we don't go this way. We use standart settings if it possible.
180+
147181
## Contributing
148182

149183
See [the contributing guide](CONTRIBUTING.md) for detailed instructions on how

call_16_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ func TestConnection_Call(t *testing.T) {
1818
defer conn.Close()
1919

2020
// Call16
21-
resp, err = conn.Call("simple_incr", []interface{}{1})
21+
resp, err = conn.Call("simple_concat", []interface{}{"1"})
2222
if err != nil {
2323
t.Errorf("Failed to use Call")
2424
}
25-
if resp.Data[0].([]interface{})[0].(uint64) != 2 {
25+
if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" {
2626
t.Errorf("result is not {{1}} : %v", resp.Data)
2727
}
2828
}
@@ -34,18 +34,18 @@ func TestCallRequest(t *testing.T) {
3434
conn := test_helpers.ConnectWithValidation(t, server, opts)
3535
defer conn.Close()
3636

37-
req := NewCallRequest("simple_incr").Args([]interface{}{1})
37+
req := NewCallRequest("simple_concat").Args([]interface{}{"1"})
3838
resp, err = conn.Do(req).Get()
3939
if err != nil {
4040
t.Errorf("Failed to use Call")
4141
}
42-
if resp.Data[0].([]interface{})[0].(uint64) != 2 {
42+
if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" {
4343
t.Errorf("result is not {{1}} : %v", resp.Data)
4444
}
4545
}
4646

4747
func TestCallRequestCode(t *testing.T) {
48-
req := NewCallRequest("simple_incrt")
48+
req := NewCallRequest("simple_concat")
4949
code := req.Code()
5050
expected := Call16RequestCode
5151
if code != int32(expected) {

call_17_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ func TestConnection_Call(t *testing.T) {
1818
defer conn.Close()
1919

2020
// Call17
21-
resp, err = conn.Call17("simple_incr", []interface{}{1})
21+
resp, err = conn.Call17("simple_concat", []interface{}{"1"})
2222
if err != nil {
2323
t.Errorf("Failed to use Call")
2424
}
25-
if resp.Data[0].(uint64) != 2 {
25+
if val, ok := resp.Data[0].(string); !ok || val != "11" {
2626
t.Errorf("result is not {{1}} : %v", resp.Data)
2727
}
2828
}
@@ -34,18 +34,18 @@ func TestCallRequest(t *testing.T) {
3434
conn := test_helpers.ConnectWithValidation(t, server, opts)
3535
defer conn.Close()
3636

37-
req := NewCallRequest("simple_incr").Args([]interface{}{1})
37+
req := NewCallRequest("simple_concat").Args([]interface{}{"1"})
3838
resp, err = conn.Do(req).Get()
3939
if err != nil {
4040
t.Errorf("Failed to use Call")
4141
}
42-
if resp.Data[0].(uint64) != 2 {
42+
if val, ok := resp.Data[0].(string); !ok || val != "11" {
4343
t.Errorf("result is not {{1}} : %v", resp.Data)
4444
}
4545
}
4646

4747
func TestCallRequestCode(t *testing.T) {
48-
req := NewCallRequest("simple_incrt")
48+
req := NewCallRequest("simple_concat")
4949
code := req.Code()
5050
expected := Call17RequestCode
5151
if code != int32(expected) {

config.lua

+4-4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ box.once("init", function()
8080

8181
--box.schema.user.grant('guest', 'read,write,execute', 'universe')
8282
box.schema.func.create('box.info')
83-
box.schema.func.create('simple_incr')
83+
box.schema.func.create('simple_concat')
8484

8585
-- auth testing: access control
8686
box.schema.user.create('test', {password = 'test'})
@@ -106,10 +106,10 @@ local function func_name()
106106
end
107107
rawset(_G, 'func_name', func_name)
108108

109-
local function simple_incr(a)
110-
return a + 1
109+
local function simple_concat(a)
110+
return a .. a
111111
end
112-
rawset(_G, 'simple_incr', simple_incr)
112+
rawset(_G, 'simple_concat', simple_concat)
113113

114114
local function push_func(cnt)
115115
for i = 1, cnt do

datetime/datetime_test.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ func assertDatetimeIsEqual(t *testing.T, tuples []interface{}, tm time.Time) {
6464
if len(tpl) != 2 {
6565
t.Fatalf("Unexpected return value body (tuple len = %d)", len(tpl))
6666
}
67-
if val, ok := tpl[dtIndex].(Datetime); !ok || !val.ToTime().Equal(tm) {
67+
if val, ok := toDatetime(tpl[dtIndex]); !ok || !val.ToTime().Equal(tm) {
6868
t.Fatalf("Unexpected tuple %d field %v, expected %v",
6969
dtIndex,
70-
val.ToTime(),
70+
val,
7171
tm)
7272
}
7373
}
@@ -324,15 +324,18 @@ func (ev *Event) DecodeMsgpack(d *decoder) error {
324324
if err != nil {
325325
return err
326326
}
327-
ev.Datetime = res.(Datetime)
327+
var ok bool
328+
if ev.Datetime, ok = toDatetime(res); !ok {
329+
return fmt.Errorf("datetime doesn't match")
330+
}
328331
return nil
329332
}
330333

331334
func (c *Tuple2) EncodeMsgpack(e *encoder) error {
332335
if err := e.EncodeArrayLen(3); err != nil {
333336
return err
334337
}
335-
if err := encodeUint(e, uint64(c.Cid)); err != nil {
338+
if err := e.EncodeUint64(uint64(c.Cid)); err != nil {
336339
return err
337340
}
338341
if err := e.EncodeString(c.Orig); err != nil {
@@ -428,8 +431,8 @@ func TestCustomEncodeDecodeTuple1(t *testing.T) {
428431
}
429432

430433
for i, tv := range []time.Time{tm1, tm2} {
431-
dt := events[i].([]interface{})[1].(Datetime)
432-
if !dt.ToTime().Equal(tv) {
434+
dt, ok := toDatetime(events[i].([]interface{})[1])
435+
if !ok || !dt.ToTime().Equal(tv) {
433436
t.Fatalf("%v != %v", dt.ToTime(), tv)
434437
}
435438
}
@@ -501,7 +504,7 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) {
501504
if tpl, ok := resp.Data[0].([]interface{}); !ok {
502505
t.Errorf("Unexpected body of Select")
503506
} else {
504-
if val, ok := tpl[0].(Datetime); !ok || !val.ToTime().Equal(tm) {
507+
if val, ok := toDatetime(tpl[0]); !ok || !val.ToTime().Equal(tm) {
505508
t.Fatalf("Unexpected body of Select")
506509
}
507510
}

datetime/msgpack.go

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//go:build !go_tarantool_msgpack_v5
2+
// +build !go_tarantool_msgpack_v5
3+
14
package datetime
25

36
import (

datetime/msgpack_helper_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
//go:build !go_tarantool_msgpack_v5
2+
// +build !go_tarantool_msgpack_v5
3+
14
package datetime_test
25

36
import (
7+
. "github.com/tarantool/go-tarantool/datetime"
8+
49
"gopkg.in/vmihailenco/msgpack.v2"
510
)
611

712
type encoder = msgpack.Encoder
813
type decoder = msgpack.Decoder
914

10-
func encodeUint(e *encoder, v uint64) error {
11-
return e.EncodeUint(uint(v))
15+
func toDatetime(i interface{}) (dt Datetime, ok bool) {
16+
dt, ok = i.(Datetime)
17+
return
1218
}
1319

1420
func marshal(v interface{}) ([]byte, error) {

datetime/msgpack_v5.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build go_tarantool_msgpack_v5
2+
// +build go_tarantool_msgpack_v5
3+
4+
package datetime
5+
6+
import (
7+
"github.com/vmihailenco/msgpack/v5"
8+
)
9+
10+
func init() {
11+
msgpack.RegisterExt(datetime_extId, (*Datetime)(nil))
12+
}

datetime/msgpack_v5_helper_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//go:build go_tarantool_msgpack_v5
2+
// +build go_tarantool_msgpack_v5
3+
4+
package datetime_test
5+
6+
import (
7+
. "github.com/tarantool/go-tarantool/datetime"
8+
"github.com/vmihailenco/msgpack/v5"
9+
)
10+
11+
type encoder = msgpack.Encoder
12+
type decoder = msgpack.Decoder
13+
14+
func toDatetime(i interface{}) (dt Datetime, ok bool) {
15+
var ptr *Datetime
16+
if ptr, ok = i.(*Datetime); ok {
17+
dt = *ptr
18+
}
19+
return
20+
}
21+
22+
func marshal(v interface{}) ([]byte, error) {
23+
return msgpack.Marshal(v)
24+
}
25+
26+
func unmarshal(data []byte, v interface{}) error {
27+
return msgpack.Unmarshal(data, v)
28+
}

decimal/decimal_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ func (t *TupleDecimal) DecodeMsgpack(d *decoder) error {
6060
if err != nil {
6161
return err
6262
}
63-
t.number = res.(Decimal)
64-
63+
var ok bool
64+
if t.number, ok = toDecimal(res); !ok {
65+
return fmt.Errorf("decimal doesn't match")
66+
}
6567
return nil
6668
}
6769

@@ -347,7 +349,7 @@ func tupleValueIsDecimal(t *testing.T, tuples []interface{}, number decimal.Deci
347349
if len(tpl) != 1 {
348350
t.Fatalf("Unexpected return value body (tuple len)")
349351
}
350-
if val, ok := tpl[0].(Decimal); !ok || !val.Equal(number) {
352+
if val, ok := toDecimal(tpl[0]); !ok || !val.Equal(number) {
351353
t.Fatalf("Unexpected return value body (tuple 0 field)")
352354
}
353355
}
@@ -445,7 +447,10 @@ func TestMPDecode(t *testing.T) {
445447
if err != nil {
446448
t.Fatalf("Unmarshalling failed: %s", err.Error())
447449
}
448-
decActual := v.(Decimal)
450+
decActual, ok := toDecimal(v)
451+
if !ok {
452+
t.Fatalf("Unable to convert to Decimal")
453+
}
449454

450455
decExpected, err := decimal.NewFromString(testcase.numString)
451456
if err != nil {

decimal/msgpack.go

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//go:build !go_tarantool_msgpack_v5
2+
// +build !go_tarantool_msgpack_v5
3+
14
package decimal
25

36
import (

decimal/msgpack_helper_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
//go:build !go_tarantool_msgpack_v5
2+
// +build !go_tarantool_msgpack_v5
3+
14
package decimal_test
25

36
import (
7+
. "github.com/tarantool/go-tarantool/decimal"
8+
49
"gopkg.in/vmihailenco/msgpack.v2"
510
)
611

712
type encoder = msgpack.Encoder
813
type decoder = msgpack.Decoder
914

15+
func toDecimal(i interface{}) (dec Decimal, ok bool) {
16+
dec, ok = i.(Decimal)
17+
return
18+
}
19+
1020
func marshal(v interface{}) ([]byte, error) {
1121
return msgpack.Marshal(v)
1222
}

decimal/msgpack_v5.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build go_tarantool_msgpack_v5
2+
// +build go_tarantool_msgpack_v5
3+
4+
package decimal
5+
6+
import (
7+
"github.com/vmihailenco/msgpack/v5"
8+
)
9+
10+
func init() {
11+
msgpack.RegisterExt(decimalExtID, (*Decimal)(nil))
12+
}

decimal/msgpack_v5_helper_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//go:build go_tarantool_msgpack_v5
2+
// +build go_tarantool_msgpack_v5
3+
4+
package decimal_test
5+
6+
import (
7+
. "github.com/tarantool/go-tarantool/decimal"
8+
"github.com/vmihailenco/msgpack/v5"
9+
)
10+
11+
type encoder = msgpack.Encoder
12+
type decoder = msgpack.Decoder
13+
14+
func toDecimal(i interface{}) (dec Decimal, ok bool) {
15+
var ptr *Decimal
16+
if ptr, ok = i.(*Decimal); ok {
17+
dec = *ptr
18+
}
19+
return
20+
}
21+
22+
func marshal(v interface{}) ([]byte, error) {
23+
return msgpack.Marshal(v)
24+
}
25+
26+
func unmarshal(data []byte, v interface{}) error {
27+
return msgpack.Unmarshal(data, v)
28+
}

0 commit comments

Comments
 (0)