Skip to content

Commit 7c3a05b

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. 1. tarantool/tarantool#4398 2. tarantool/tarantool#6433 Closes #209
1 parent 0d431a6 commit 7c3a05b

12 files changed

+582
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
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

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
)
66

7+
const errorExtID = 3
8+
79
// BoxError is a type representing Tarantool `box.error` object: a single
810
// MP_ERROR_STACK object with a link to the previous stack error.
911
// See https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_error/error/
@@ -38,8 +40,6 @@ func (boxError *BoxError) Error() string {
3840
}
3941

4042
// Depth computes the count of errors in stack, including the current one.
41-
//
42-
// Since 1.10.0
4343
func (boxError *BoxError) Depth() (depth int) {
4444
cur := boxError
4545

@@ -149,3 +149,126 @@ func decodeBoxError(d *decoder) (*BoxError, error) {
149149

150150
return nil, nil
151151
}
152+
153+
func encodeBoxError(enc *encoder, boxError *BoxError) (err error) {
154+
if err = enc.EncodeMapLen(1); err != nil {
155+
return err
156+
}
157+
if err = encodeUint(enc, KeyErrorStack); err != nil {
158+
return err
159+
}
160+
161+
var stackDepth = boxError.Depth()
162+
if err = enc.EncodeArrayLen(stackDepth); err != nil {
163+
return err
164+
}
165+
166+
for ; stackDepth > 0; stackDepth-- {
167+
fieldsLen := len(boxError.Fields)
168+
169+
if fieldsLen > 0 {
170+
if err = enc.EncodeMapLen(7); err != nil {
171+
return err
172+
}
173+
} else {
174+
if err = enc.EncodeMapLen(6); err != nil {
175+
return err
176+
}
177+
}
178+
179+
if err = encodeUint(enc, KeyErrorType); err != nil {
180+
return err
181+
}
182+
if err = enc.EncodeString(boxError.Type); err != nil {
183+
return err
184+
}
185+
186+
if err = encodeUint(enc, KeyErrorFile); err != nil {
187+
return err
188+
}
189+
if err = enc.EncodeString(boxError.File); err != nil {
190+
return err
191+
}
192+
193+
if err = encodeUint(enc, KeyErrorLine); err != nil {
194+
return err
195+
}
196+
if err = enc.EncodeUint32(boxError.Line); err != nil {
197+
return err
198+
}
199+
200+
if err = encodeUint(enc, KeyErrorMessage); err != nil {
201+
return err
202+
}
203+
if err = enc.EncodeString(boxError.Msg); err != nil {
204+
return err
205+
}
206+
207+
if err = encodeUint(enc, KeyErrorErrno); err != nil {
208+
return err
209+
}
210+
if err = enc.EncodeUint32(boxError.Errno); err != nil {
211+
return err
212+
}
213+
214+
if err = encodeUint(enc, KeyErrorErrcode); err != nil {
215+
return err
216+
}
217+
if err = enc.EncodeUint32(boxError.Code); err != nil {
218+
return err
219+
}
220+
221+
if fieldsLen > 0 {
222+
if err = encodeUint(enc, KeyErrorFields); err != nil {
223+
return err
224+
}
225+
226+
if err = enc.EncodeMapLen(fieldsLen); err != nil {
227+
return err
228+
}
229+
230+
for k, v := range boxError.Fields {
231+
if err = enc.Encode(k); err != nil {
232+
return err
233+
}
234+
if err = enc.Encode(v); err != nil {
235+
return err
236+
}
237+
}
238+
}
239+
240+
if stackDepth > 1 {
241+
boxError = boxError.Prev
242+
}
243+
}
244+
245+
return nil
246+
}
247+
248+
// MarshalMsgpack serializes the BoxError into a MessagePack representation.
249+
func (boxError *BoxError) MarshalMsgpack() (b []byte, err error) {
250+
var buf smallWBuf
251+
252+
enc := newEncoder(&buf)
253+
if err = encodeBoxError(enc, boxError); err != nil {
254+
return b, err
255+
}
256+
257+
return buf.b, nil
258+
}
259+
260+
// UnmarshalMsgpack deserializes a BoxError value from a MessagePack
261+
// representation.
262+
func (boxError *BoxError) UnmarshalMsgpack(b []byte) error {
263+
var buf smallBuf = smallBuf{b: b}
264+
265+
dec := newDecoder(&buf)
266+
val, err := decodeBoxError(dec)
267+
if err != nil {
268+
return err
269+
}
270+
271+
*boxError = *val
272+
273+
return nil
274+
}

0 commit comments

Comments
 (0)