Skip to content

Commit 18e838c

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 18e838c

12 files changed

+579
-7
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

+122
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/
@@ -149,3 +151,123 @@ func decodeBoxError(d *decoder) (*BoxError, error) {
149151

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

0 commit comments

Comments
 (0)