diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1f304a9..171d19216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ CI and documentation. - queue-utube handling (#85) - Master discovery (#113) - SQL support (#62) +- Optional msgpack.v5 usage (#124) ### Changed diff --git a/README.md b/README.md index a2b77eaa7..fced5e485 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) @@ -54,10 +55,22 @@ of any Go program. ### Build tags -To disable SSL support and linking with OpenSSL, you can use the tag: -``` -go_tarantool_ssl_disable -``` +We define multiple [build tags](https://pkg.go.dev/go/build#hdr-Build_Constraints). + +This allows us to introduce new features without losing backward compatibility. + +1. To disable SSL support and linking with OpenSSL, you can use the tag: + ``` + go_tarantool_ssl_disable + ``` +2. 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. ## Documentation @@ -129,6 +142,30 @@ 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 (except at least `EncodeInt`, `EncodeUint` and `RegisterExt`). +But there are a lot of changes in a logic of encoding and deconding. 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`. The change makes manual type conversions much more difficult and can +lead to runtime erros with an old code. We recommend not 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` has added some functions for the logic tuning 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, but 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/go.mod b/go.mod index 6dcaee974..37b7e1e41 100644 --- a/go.mod +++ b/go.mod @@ -10,4 +10,5 @@ require ( google.golang.org/appengine v1.6.7 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/vmihailenco/msgpack.v2 v2.9.2 + github.com/vmihailenco/msgpack/v5 v5.3.5 ) diff --git a/go.sum b/go.sum index 1af7f9933..3d4d252cd 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,15 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 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.20220419150948-be4921aa2f87 h1:JGzuBxNBq5saVtPUcuu5Y4+kbJON6H02//OT+RNqGts= github.com/tarantool/go-openssl v0.0.8-0.20220419150948-be4921aa2f87/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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= diff --git a/msgpack.go b/msgpack.go index 11f0f4aa2..ac271d734 100644 --- a/msgpack.go +++ b/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package tarantool import ( @@ -54,53 +57,53 @@ func msgpackIsString(code byte) bool { } func (s *single) DecodeMsgpack(d *msgpack.Decoder) error { - return s.decodeMsgpackImpl(&Decoder{d}) + return s.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error { - return space.decodeMsgpackImpl(&Decoder{d}) + return space.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (field *Field) DecodeMsgpack(d *msgpack.Decoder) error { - return field.decodeMsgpackImpl(&Decoder{d}) + return field.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { - return index.decodeMsgpackImpl(&Decoder{d}) + return index.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { - return indexField.decodeMsgpackImpl(&Decoder{d}) + return indexField.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (meta *ColumnMetaData) DecodeMsgpack(d *msgpack.Decoder) error { - return meta.decodeMsgpackImpl(&Decoder{d}) + return meta.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (info *SQLInfo) DecodeMsgpack(d *msgpack.Decoder) error { - return info.decodeMsgpackImpl(&Decoder{d}) + return info.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (k IntKey) EncodeMsgpack(enc *msgpack.Encoder) error { - return k.encodeMsgpackImpl(&Encoder{enc}) + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) } func (k UintKey) EncodeMsgpack(enc *msgpack.Encoder) error { - return k.encodeMsgpackImpl(&Encoder{enc}) + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) } func (k StringKey) EncodeMsgpack(enc *msgpack.Encoder) error { - return k.encodeMsgpackImpl(&Encoder{enc}) + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) } func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error { - return k.encodeMsgpackImpl(&Encoder{enc}) + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) } func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error { - return o.encodeMsgpackImpl(&Encoder{enc}) + return o.encodeMsgpackImpl(&Encoder{Encoder: enc}) } func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error { - return o.encodeMsgpackImpl(&Encoder{enc}) + return o.encodeMsgpackImpl(&Encoder{Encoder: enc}) } diff --git a/msgpack_helper_test.go b/msgpack_helper_test.go index d7f7346f1..d7574feca 100644 --- a/msgpack_helper_test.go +++ b/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package tarantool_test import ( @@ -6,17 +9,17 @@ import ( ) func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { - return m.encodeMsgpackImpl(&Encoder{e}) + return m.encodeMsgpackImpl(&Encoder{Encoder: e}) } func (m *Member) DecodeMsgpack(d *msgpack.Decoder) error { - return m.decodeMsgpackImpl(&Decoder{d}) + return m.decodeMsgpackImpl(&Decoder{Decoder: d}) } func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { - return c.encodeMsgpackImpl(&Encoder{e}) + return c.encodeMsgpackImpl(&Encoder{Encoder: e}) } func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { - return c.decodeMsgpackImpl(&Decoder{d}) + return c.decodeMsgpackImpl(&Decoder{Decoder: d}) } diff --git a/msgpack_v5.go b/msgpack_v5.go new file mode 100644 index 000000000..2c790bddc --- /dev/null +++ b/msgpack_v5.go @@ -0,0 +1,112 @@ +//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 struct { + *msgpack.Encoder +} + +type Decoder struct { + *msgpack.Decoder +} + +func newEncoder(w io.Writer) *Encoder { + enc := msgpack.NewEncoder(w) + return &Encoder{Encoder: enc} +} + +func newDecoder(r io.Reader) *Decoder { + dec := msgpack.NewDecoder(r) + dec.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) + return &Decoder{Decoder: dec} +} + +func (e *Encoder) encodeUintImpl(v uint64) error { + return e.EncodeUint(v) +} + +func (e *Encoder) encodeIntImpl(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 +} + +func (s *single) DecodeMsgpack(d *msgpack.Decoder) error { + return s.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error { + return space.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (field *Field) DecodeMsgpack(d *msgpack.Decoder) error { + return field.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { + return index.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { + return indexField.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (meta *ColumnMetaData) DecodeMsgpack(d *msgpack.Decoder) error { + return meta.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (info *SQLInfo) DecodeMsgpack(d *msgpack.Decoder) error { + return info.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (k IntKey) EncodeMsgpack(enc *msgpack.Encoder) error { + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} + +func (k UintKey) EncodeMsgpack(enc *msgpack.Encoder) error { + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} + +func (k StringKey) EncodeMsgpack(enc *msgpack.Encoder) error { + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} + +func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error { + return k.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} + +func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error { + return o.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} + +func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error { + return o.encodeMsgpackImpl(&Encoder{Encoder: enc}) +} diff --git a/msgpack_v5_helper_test.go b/msgpack_v5_helper_test.go new file mode 100644 index 000000000..b74fda260 --- /dev/null +++ b/msgpack_v5_helper_test.go @@ -0,0 +1,25 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package tarantool_test + +import ( + . "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error { + return m.encodeMsgpackImpl(&Encoder{Encoder: e}) +} + +func (m *Member) DecodeMsgpack(d *msgpack.Decoder) error { + return m.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (c *Tuple2) EncodeMsgpack(e *msgpack.Encoder) error { + return c.encodeMsgpackImpl(&Encoder{Encoder: e}) +} + +func (c *Tuple2) DecodeMsgpack(d *msgpack.Decoder) error { + return c.decodeMsgpackImpl(&Decoder{Decoder: d}) +} diff --git a/queue/msgpack.go b/queue/msgpack.go index a1696c7d5..2c4f5debc 100644 --- a/queue/msgpack.go +++ b/queue/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package queue import ( diff --git a/queue/msgpack_helper_test.go b/queue/msgpack_helper_test.go index 47c3c26f7..4b83d2d27 100644 --- a/queue/msgpack_helper_test.go +++ b/queue/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package queue_test import ( diff --git a/queue/msgpack_v5.go b/queue/msgpack_v5.go new file mode 100644 index 000000000..c7c417e32 --- /dev/null +++ b/queue/msgpack_v5.go @@ -0,0 +1,17 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package queue + +import ( + "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +func (qd *queueData) DecodeMsgpack(d *msgpack.Decoder) error { + return qd.decodeMsgpackImpl(&tarantool.Decoder{Decoder: d}) +} + +func (t *Task) DecodeMsgpack(d *msgpack.Decoder) error { + return t.decodeMsgpackImpl(&tarantool.Decoder{Decoder: d}) +} diff --git a/queue/msgpack_v5_helper_test.go b/queue/msgpack_v5_helper_test.go new file mode 100644 index 000000000..c970d73b2 --- /dev/null +++ b/queue/msgpack_v5_helper_test.go @@ -0,0 +1,25 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package queue_test + +import ( + . "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error { + return c.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (c *customData) EncodeMsgpack(e *msgpack.Encoder) error { + return c.encodeMsgpackImpl(&Encoder{Encoder: e}) +} + +func (c *dummyData) DecodeMsgpack(d *msgpack.Decoder) error { + return c.decodeMsgpackImpl(&Decoder{Decoder: d}) +} + +func (c *dummyData) EncodeMsgpack(e *msgpack.Encoder) error { + return c.encodeMsgpackImpl(&Encoder{Encoder: e}) +} diff --git a/uuid/msgpack.go b/uuid/msgpack.go index 6dc4de478..37906cc4c 100644 --- a/uuid/msgpack.go +++ b/uuid/msgpack.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package uuid import ( diff --git a/uuid/msgpack_helper_test.go b/uuid/msgpack_helper_test.go index 1a36ab828..94828c3c8 100644 --- a/uuid/msgpack_helper_test.go +++ b/uuid/msgpack_helper_test.go @@ -1,3 +1,6 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + package uuid_test import ( diff --git a/uuid/msgpack_v5.go b/uuid/msgpack_v5.go new file mode 100644 index 000000000..b65ff1b83 --- /dev/null +++ b/uuid/msgpack_v5.go @@ -0,0 +1,33 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package uuid + +import ( + "reflect" + + "github.com/google/uuid" + . "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +func encodeUUID(e *msgpack.Encoder, v reflect.Value) error { + return encodeUUIDImpl(&Encoder{Encoder: e}, v) +} + +func decodeUUID(d *msgpack.Decoder, v reflect.Value) error { + return decodeUUIDImpl(&Decoder{Decoder: d}, v) +} + +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..caac761e0 --- /dev/null +++ b/uuid/msgpack_v5_helper_test.go @@ -0,0 +1,13 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package uuid_test + +import ( + . "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +func (t *TupleUUID) DecodeMsgpack(d *msgpack.Decoder) error { + return t.decodeMsgpackImpl(&Decoder{Decoder: d}) +}