Skip to content

Commit a3bc8c4

Browse files
api: support errors extra information
Since Tarantool 2.4.1, iproto error responses contain extra info with backtrace [1]. After this patch, Error would contain ExtraInfo field (BoxError object), if it was provided. 1. https://www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol/#responses-for-errors Part of #209
1 parent 7592b93 commit a3bc8c4

File tree

7 files changed

+358
-5
lines changed

7 files changed

+358
-5
lines changed

CHANGELOG.md

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

1313
- Support iproto feature discovery (#120).
14+
- Support errors extra information (#209).
1415

1516
### Changed
1617

box_error.go

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package tarantool
2+
3+
// BoxError is a type representing Tarantool `box.error` object: a single
4+
// MP_ERROR_STACK object with a link to the previous stack error.
5+
type BoxError struct {
6+
Type string // Type that implies source, for example "ClientError".
7+
File string // Source code file where error was caught.
8+
Line int64 // Line number in source code file.
9+
Message string // Text of reason.
10+
Errno int64 // Ordinal number of the error.
11+
Errcode int64 // Number of the error as defined in `errcode.h`.
12+
// Additional fields depending on error type. For example, if
13+
// type is "AccessDeniedError", then it will include "object_type",
14+
// "object_name", "access_type".
15+
Fields map[interface{}]interface{}
16+
Prev *BoxError // Previous error in stack.
17+
}
18+
19+
func (err BoxError) Depth() (depth int) {
20+
depth = 1
21+
for err.Prev != nil {
22+
err = *err.Prev
23+
depth++
24+
}
25+
return depth
26+
}
27+
28+
func decodeBoxErrorStack(d *decoder) (*BoxError, error) {
29+
var l, larr, l1, l2 int
30+
var errorStack []BoxError
31+
var err error
32+
var mapk, mapv interface{}
33+
34+
if l, err = d.DecodeMapLen(); err != nil {
35+
return nil, err
36+
}
37+
38+
for ; l > 0; l-- {
39+
var cd int
40+
if cd, err = d.DecodeInt(); err != nil {
41+
return nil, err
42+
}
43+
switch cd {
44+
case KeyErrorStack:
45+
if larr, err = d.DecodeArrayLen(); err != nil {
46+
return nil, err
47+
}
48+
49+
errorStack = make([]BoxError, larr)
50+
51+
for i := 0; i < larr; i++ {
52+
if l1, err = d.DecodeMapLen(); err != nil {
53+
return nil, err
54+
}
55+
56+
for ; l1 > 0; l1-- {
57+
var cd1 int
58+
if cd1, err = d.DecodeInt(); err != nil {
59+
return nil, err
60+
}
61+
switch cd1 {
62+
case KeyErrorType:
63+
if errorStack[i].Type, err = d.DecodeString(); err != nil {
64+
return nil, err
65+
}
66+
case KeyErrorFile:
67+
if errorStack[i].File, err = d.DecodeString(); err != nil {
68+
return nil, err
69+
}
70+
case KeyErrorLine:
71+
if errorStack[i].Line, err = d.DecodeInt64(); err != nil {
72+
return nil, err
73+
}
74+
case KeyErrorMessage:
75+
if errorStack[i].Message, err = d.DecodeString(); err != nil {
76+
return nil, err
77+
}
78+
case KeyErrorErrno:
79+
if errorStack[i].Errno, err = d.DecodeInt64(); err != nil {
80+
return nil, err
81+
}
82+
case KeyErrorErrcode:
83+
if errorStack[i].Errcode, err = d.DecodeInt64(); err != nil {
84+
return nil, err
85+
}
86+
case KeyErrorFields:
87+
errorStack[i].Fields = make(map[interface{}]interface{})
88+
if l2, err = d.DecodeMapLen(); err != nil {
89+
return nil, err
90+
}
91+
for ; l2 > 0; l2-- {
92+
if mapk, err = d.DecodeInterface(); err != nil {
93+
return nil, err
94+
}
95+
if mapv, err = d.DecodeInterface(); err != nil {
96+
return nil, err
97+
}
98+
errorStack[i].Fields[mapk] = mapv
99+
}
100+
default:
101+
if err = d.Skip(); err != nil {
102+
return nil, err
103+
}
104+
}
105+
}
106+
107+
if i > 0 {
108+
errorStack[i-1].Prev = &errorStack[i]
109+
}
110+
}
111+
default:
112+
if err = d.Skip(); err != nil {
113+
return nil, err
114+
}
115+
}
116+
}
117+
118+
if len(errorStack) > 0 {
119+
return &errorStack[0], nil
120+
}
121+
122+
return nil, nil
123+
}
124+
125+
// func encodeBoxError(enc *encoder, err BoxError) (*BoxError, error) {
126+
// var l, larr, l1, l2 int
127+
// var errorStack []BoxError
128+
// var err error
129+
// var mapk, mapv interface{}
130+
131+
// if l, err = d.DecodeMapLen(); err != nil {
132+
// return nil, err
133+
// }
134+
135+
// for ; l > 0; l-- {
136+
// var cd int
137+
// if cd, err = d.DecodeInt(); err != nil {
138+
// return nil, err
139+
// }
140+
// switch cd {
141+
// case KeyErrorStack:
142+
// if larr, err = d.DecodeArrayLen(); err != nil {
143+
// return nil, err
144+
// }
145+
146+
// errorStack = make([]BoxError, larr)
147+
148+
// for i := 0; i < larr; i++ {
149+
// if l1, err = d.DecodeMapLen(); err != nil {
150+
// return nil, err
151+
// }
152+
153+
// for ; l1 > 0; l1-- {
154+
// var cd1 int
155+
// if cd1, err = d.DecodeInt(); err != nil {
156+
// return nil, err
157+
// }
158+
// switch cd1 {
159+
// case KeyErrorType:
160+
// if errorStack[i].Type, err = d.DecodeString(); err != nil {
161+
// return nil, err
162+
// }
163+
// case KeyErrorFile:
164+
// if errorStack[i].File, err = d.DecodeString(); err != nil {
165+
// return nil, err
166+
// }
167+
// case KeyErrorLine:
168+
// if errorStack[i].Line, err = d.DecodeInt64(); err != nil {
169+
// return nil, err
170+
// }
171+
// case KeyErrorMessage:
172+
// if errorStack[i].Message, err = d.DecodeString(); err != nil {
173+
// return nil, err
174+
// }
175+
// case KeyErrorErrno:
176+
// if errorStack[i].Errno, err = d.DecodeInt64(); err != nil {
177+
// return nil, err
178+
// }
179+
// case KeyErrorErrcode:
180+
// if errorStack[i].Errcode, err = d.DecodeInt64(); err != nil {
181+
// return nil, err
182+
// }
183+
// case KeyErrorFields:
184+
// errorStack[i].Fields = make(map[interface{}]interface{})
185+
// if l2, err = d.DecodeMapLen(); err != nil {
186+
// return nil, err
187+
// }
188+
// for ; l2 > 0; l2-- {
189+
// if mapk, err = d.DecodeInterface(); err != nil {
190+
// return nil, err
191+
// }
192+
// if mapv, err = d.DecodeInterface(); err != nil {
193+
// return nil, err
194+
// }
195+
// errorStack[i].Fields[mapk] = mapv
196+
// }
197+
// default:
198+
// if err = d.Skip(); err != nil {
199+
// return nil, err
200+
// }
201+
// }
202+
// }
203+
204+
// if i > 0 {
205+
// errorStack[i-1].Prev = &errorStack[i]
206+
// }
207+
// }
208+
// default:
209+
// if err = d.Skip(); err != nil {
210+
// return nil, err
211+
// }
212+
// }
213+
// }
214+
215+
// if len(errorStack) > 0 {
216+
// return &errorStack[0], nil
217+
// }
218+
219+
// return nil, nil
220+
// }

const.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ const (
3535
KeyExpression = 0x27
3636
KeyDefTuple = 0x28
3737
KeyData = 0x30
38-
KeyError24 = 0x31
38+
KeyError24 = 0x31 /* Error in pre-2.4 format */
3939
KeyMetaData = 0x32
4040
KeyBindCount = 0x34
4141
KeySQLText = 0x40
4242
KeySQLBind = 0x41
4343
KeySQLInfo = 0x42
4444
KeyStmtID = 0x43
45+
KeyError = 0x52 /* Extended error in >= 2.4 format. */
4546
KeyVersion = 0x54
4647
KeyFeatures = 0x55
4748
KeyTimeout = 0x56
@@ -56,6 +57,15 @@ const (
5657
KeySQLInfoRowCount = 0x00
5758
KeySQLInfoAutoincrementIds = 0x01
5859

60+
KeyErrorStack = 0x00
61+
KeyErrorType = 0x00
62+
KeyErrorFile = 0x01
63+
KeyErrorLine = 0x02
64+
KeyErrorMessage = 0x03
65+
KeyErrorErrno = 0x04
66+
KeyErrorErrcode = 0x05
67+
KeyErrorFields = 0x06
68+
5969
// https://github.com/fl00r/go-tarantool-1.6/issues/2
6070

6171
IterEq = uint32(0) // key == x ASC order

errors.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import "fmt"
44

55
// Error is wrapper around error returned by Tarantool.
66
type Error struct {
7-
Code uint32
8-
Msg string
7+
Code uint32
8+
Msg string
9+
ExtraInfo *BoxError
910
}
1011

1112
// Error converts an Error to a string.

response.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func (resp *Response) decodeBody() (err error) {
151151
var stmtID, bindCount uint64
152152
var serverProtocolInfo ProtocolInfo
153153
var feature ProtocolFeature
154+
var extraErrorInfo *BoxError = nil
154155

155156
d := newDecoder(&resp.buf)
156157

@@ -176,6 +177,10 @@ func (resp *Response) decodeBody() (err error) {
176177
if resp.Error, err = d.DecodeString(); err != nil {
177178
return err
178179
}
180+
case KeyError:
181+
if extraErrorInfo, err = decodeBoxErrorStack(d); err != nil {
182+
return err
183+
}
179184
case KeySQLInfo:
180185
if err = d.Decode(&resp.SQLInfo); err != nil {
181186
return err
@@ -236,7 +241,7 @@ func (resp *Response) decodeBody() (err error) {
236241

237242
if resp.Code != OkCode && resp.Code != PushCode {
238243
resp.Code &^= ErrorCodeBit
239-
err = Error{resp.Code, resp.Error}
244+
err = Error{resp.Code, resp.Error, extraErrorInfo}
240245
}
241246
}
242247
return
@@ -248,6 +253,8 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) {
248253
defer resp.buf.Seek(offset)
249254

250255
var l int
256+
var extraErrorInfo *BoxError = nil
257+
251258
d := newDecoder(&resp.buf)
252259
if l, err = d.DecodeMapLen(); err != nil {
253260
return err
@@ -266,6 +273,10 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) {
266273
if resp.Error, err = d.DecodeString(); err != nil {
267274
return err
268275
}
276+
case KeyError:
277+
if extraErrorInfo, err = decodeBoxErrorStack(d); err != nil {
278+
return err
279+
}
269280
case KeySQLInfo:
270281
if err = d.Decode(&resp.SQLInfo); err != nil {
271282
return err
@@ -282,7 +293,7 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) {
282293
}
283294
if resp.Code != OkCode && resp.Code != PushCode {
284295
resp.Code &^= ErrorCodeBit
285-
err = Error{resp.Code, resp.Error}
296+
err = Error{resp.Code, resp.Error, extraErrorInfo}
286297
}
287298
}
288299
return

0 commit comments

Comments
 (0)