Skip to content

Commit e6c5a3f

Browse files
api: support error type in MessagePack
Tarantool supports error extension type since version 2.4.1 [1], encoding was introduced in Tarantool 2.10.0 [2]. This patch introduces the support of Tarantool error extension type in msgpack decoders and encoders. Tarantool error extension type objects are decoded to `*tarantool.BoxError` type. `*tarantool.BoxError` may be encoded to Tarantool error extension type objects. Error extension type internals are the same as errors extended information: the only difference is that extra information is encoded as a separate error dictionary field and error extension type objects are encoded as MessagePack extension type objects. The only way to receive an error extension type object from Tarantool is to receive an explicitly built `box.error` object: either from `return box.error.new(...)` or a tuple with it. All errors raised within Tarantool (including those raised with `box.error(...)`) are encoded based on the same rules as simple errors due to backward compatibility. It is possible to create error extension type objects with Go code, but it not likely to be really useful since most of their fields is computed on error initialization on the server side (even for custom error types). This patch also adds ErrorExtensionFeature flag to client protocol features list. Without this flag, all `box.error` object sent over iproto are encoded to string. We behave like Tarantool `net.box` here: if we support the feature, we provide the feature flag. Since it may become too complicated to enable/disable feature flag through import, error extension type is available as a part of the base package, in contrary to Decimal, UUID, Datetime and Interval types which are enabled by importing underscore subpackage. 1. tarantool/tarantool#4398 2. tarantool/tarantool#6433 Closes #209
1 parent e4ff6eb commit e6c5a3f

12 files changed

+536
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1212

1313
- Support iproto feature discovery (#120).
1414
- Support errors extended information (#209).
15+
- Error type support in MessagePack (#209).
1516

1617
### Changed
1718

box_error.go

+117-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"fmt"
66
)
77

8+
const errorExtID = 3
9+
810
const (
911
keyErrorStack = 0x00
1012
keyErrorType = 0x00
@@ -167,25 +169,132 @@ func decodeBoxError(d *decoder) (*BoxError, error) {
167169
return &errorStack[0], nil
168170
}
169171

172+
func encodeBoxError(enc *encoder, boxError *BoxError) error {
173+
if boxError == nil {
174+
return fmt.Errorf("msgpack: unexpected nil BoxError on encode")
175+
}
176+
177+
if err := enc.EncodeMapLen(1); err != nil {
178+
return err
179+
}
180+
if err := encodeUint(enc, keyErrorStack); err != nil {
181+
return err
182+
}
183+
184+
var stackDepth = boxError.Depth()
185+
if err := enc.EncodeArrayLen(stackDepth); err != nil {
186+
return err
187+
}
188+
189+
for ; stackDepth > 0; stackDepth-- {
190+
fieldsLen := len(boxError.Fields)
191+
192+
if fieldsLen > 0 {
193+
if err := enc.EncodeMapLen(7); err != nil {
194+
return err
195+
}
196+
} else {
197+
if err := enc.EncodeMapLen(6); err != nil {
198+
return err
199+
}
200+
}
201+
202+
if err := encodeUint(enc, keyErrorType); err != nil {
203+
return err
204+
}
205+
if err := enc.EncodeString(boxError.Type); err != nil {
206+
return err
207+
}
208+
209+
if err := encodeUint(enc, keyErrorFile); err != nil {
210+
return err
211+
}
212+
if err := enc.EncodeString(boxError.File); err != nil {
213+
return err
214+
}
215+
216+
if err := encodeUint(enc, keyErrorLine); err != nil {
217+
return err
218+
}
219+
if err := enc.EncodeUint64(boxError.Line); err != nil {
220+
return err
221+
}
222+
223+
if err := encodeUint(enc, keyErrorMessage); err != nil {
224+
return err
225+
}
226+
if err := enc.EncodeString(boxError.Msg); err != nil {
227+
return err
228+
}
229+
230+
if err := encodeUint(enc, keyErrorErrno); err != nil {
231+
return err
232+
}
233+
if err := enc.EncodeUint64(boxError.Errno); err != nil {
234+
return err
235+
}
236+
237+
if err := encodeUint(enc, keyErrorErrcode); err != nil {
238+
return err
239+
}
240+
if err := enc.EncodeUint64(boxError.Code); err != nil {
241+
return err
242+
}
243+
244+
if fieldsLen > 0 {
245+
if err := encodeUint(enc, keyErrorFields); err != nil {
246+
return err
247+
}
248+
249+
if err := enc.EncodeMapLen(fieldsLen); err != nil {
250+
return err
251+
}
252+
253+
for k, v := range boxError.Fields {
254+
if err := enc.EncodeString(k); err != nil {
255+
return err
256+
}
257+
if err := enc.Encode(v); err != nil {
258+
return err
259+
}
260+
}
261+
}
262+
263+
if stackDepth > 1 {
264+
boxError = boxError.Prev
265+
}
266+
}
267+
268+
return nil
269+
}
270+
170271
// UnmarshalMsgpack deserializes a BoxError value from a MessagePack
171272
// representation.
172273
func (e *BoxError) UnmarshalMsgpack(b []byte) error {
173-
var val *BoxError
174-
var err error
175-
176274
buf := bytes.NewBuffer(b)
177275
dec := newDecoder(buf)
178276

179-
if val, err = decodeBoxError(dec); err != nil {
277+
if val, err := decodeBoxError(dec); err != nil {
180278
return err
181-
}
182-
183-
if val != nil {
279+
} else {
184280
if e == nil {
185281
e = &BoxError{}
186282
}
283+
187284
*e = *val
285+
286+
return nil
188287
}
288+
}
189289

190-
return nil
290+
// MarshalMsgpack serializes the BoxError into a MessagePack representation.
291+
func (e *BoxError) MarshalMsgpack() ([]byte, error) {
292+
var buf bytes.Buffer
293+
294+
enc := newEncoder(&buf)
295+
if err := encodeBoxError(enc, e); err != nil {
296+
return nil, err
297+
}
298+
299+
return buf.Bytes(), nil
191300
}

0 commit comments

Comments
 (0)