Skip to content

Commit a6bbc72

Browse files
crud: support schema
Support `crud.schema` request [1] and response parsing. 1. tarantool/crud#380
1 parent 852ec7e commit a6bbc72

File tree

4 files changed

+465
-1
lines changed

4 files changed

+465
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1919
- Support `operation_data` in `crud.Error` (#330)
2020
- Support `fetch_latest_metadata` option for crud requests with metadata (#335)
2121
- Support `noreturn` option for data change crud requests (#335)
22+
- Support `crud.schema` request (#???)
2223

2324
### Changed
2425

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ clean:
2727
.PHONY: deps
2828
deps: clean
2929
( cd ./queue/testdata; $(TTCTL) rocks install queue 1.3.0 )
30-
( cd ./crud/testdata; $(TTCTL) rocks install crud 1.3.0 )
30+
( cd ./crud/testdata; $(TTCTL) rocks install crud 1.4.0 ) # crud 1.4.0 is yet to be released
3131

3232
.PHONY: datetime-timezones
3333
datetime-timezones:

crud/schema.go

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
package crud
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/vmihailenco/msgpack/v5"
8+
"github.com/vmihailenco/msgpack/v5/msgpcode"
9+
10+
"github.com/tarantool/go-tarantool/v2"
11+
)
12+
13+
func msgpackIsMap(code byte) bool {
14+
return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code)
15+
}
16+
17+
// SchemaRequest helps you to create request object to call `crud.schema`
18+
// for execution by a Connection.
19+
type SchemaRequest struct {
20+
baseRequest
21+
space OptString
22+
}
23+
24+
// MakeSchemaRequest returns a new empty StatsRequest.
25+
func MakeSchemaRequest() SchemaRequest {
26+
req := SchemaRequest{}
27+
req.impl = newCall("crud.schema")
28+
return req
29+
}
30+
31+
// Space sets the space name for the StatsRequest request.
32+
// Note: default value is nil.
33+
func (req SchemaRequest) Space(space string) SchemaRequest {
34+
req.space = MakeOptString(space)
35+
return req
36+
}
37+
38+
// Body fills an encoder with the call request body.
39+
func (req SchemaRequest) Body(res tarantool.SchemaResolver, enc *msgpack.Encoder) error {
40+
if value, ok := req.space.Get(); ok {
41+
req.impl = req.impl.Args([]interface{}{value})
42+
} else {
43+
req.impl = req.impl.Args([]interface{}{})
44+
}
45+
46+
return req.impl.Body(res, enc)
47+
}
48+
49+
// Context sets a passed context to CRUD request.
50+
func (req SchemaRequest) Context(ctx context.Context) SchemaRequest {
51+
req.impl = req.impl.Context(ctx)
52+
53+
return req
54+
}
55+
56+
// Schema contains CRUD cluster schema definition.
57+
type Schema map[string]SpaceSchema
58+
59+
// DecodeMsgpack provides custom msgpack decoder.
60+
func (schema *Schema) DecodeMsgpack(d *msgpack.Decoder) error {
61+
var l int
62+
63+
code, err := d.PeekCode()
64+
if err != nil {
65+
return err
66+
}
67+
68+
if msgpackIsArray(code) {
69+
// Process empty schema case.
70+
l, err = d.DecodeArrayLen()
71+
if l != 0 {
72+
return fmt.Errorf("expected map or empty array, got non-empty array")
73+
}
74+
} else if msgpackIsMap(code) {
75+
l, err := d.DecodeMapLen()
76+
if err != nil {
77+
return err
78+
}
79+
*schema = make(map[string]SpaceSchema, l)
80+
81+
for i := 0; i < l; i++ {
82+
key, err := d.DecodeString()
83+
if err != nil {
84+
return err
85+
}
86+
87+
var spaceSchema SpaceSchema
88+
if err := d.Decode(&spaceSchema); err != nil {
89+
return err
90+
}
91+
92+
(*schema)[key] = spaceSchema
93+
}
94+
} else {
95+
return fmt.Errorf("unexpected code=%d decoding map or empty array", code)
96+
}
97+
98+
return nil
99+
}
100+
101+
// SpaceSchema contains a single CRUD space schema definition.
102+
type SpaceSchema struct {
103+
Format []FieldFormat
104+
Indexes map[uint32]Index
105+
}
106+
107+
// DecodeMsgpack provides custom msgpack decoder.
108+
func (spaceSchema *SpaceSchema) DecodeMsgpack(d *msgpack.Decoder) error {
109+
l, err := d.DecodeMapLen()
110+
if err != nil {
111+
return err
112+
}
113+
114+
for i := 0; i < l; i++ {
115+
key, err := d.DecodeString()
116+
if err != nil {
117+
return err
118+
}
119+
120+
switch key {
121+
case "format":
122+
la, err := d.DecodeArrayLen()
123+
if err != nil {
124+
return err
125+
}
126+
127+
(*spaceSchema).Format = make([]FieldFormat, la)
128+
for j := 0; j < la; j++ {
129+
if err := d.Decode(&spaceSchema.Format[j]); err != nil {
130+
return err
131+
}
132+
}
133+
case "indexes":
134+
lm, err := d.DecodeMapLen()
135+
if err != nil {
136+
return err
137+
}
138+
139+
spaceSchema.Indexes = make(map[uint32]Index, lm)
140+
for j := 0; j < lm; j++ {
141+
indexId, err := d.DecodeUint32()
142+
if err != nil {
143+
return err
144+
}
145+
146+
var indexObj Index
147+
if err := d.Decode(&indexObj); err != nil {
148+
return err
149+
}
150+
151+
spaceSchema.Indexes[indexId] = indexObj
152+
}
153+
default:
154+
if err := d.Skip(); err != nil {
155+
return err
156+
}
157+
}
158+
}
159+
160+
return nil
161+
}
162+
163+
// Index contains a CRUD space index definition.
164+
type Index struct {
165+
Id uint32
166+
Name string
167+
Type string
168+
Unique bool
169+
Parts []IndexPart
170+
}
171+
172+
// DecodeMsgpack provides custom msgpack decoder.
173+
func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error {
174+
l, err := d.DecodeMapLen()
175+
if err != nil {
176+
return err
177+
}
178+
179+
for i := 0; i < l; i++ {
180+
key, err := d.DecodeString()
181+
if err != nil {
182+
return err
183+
}
184+
185+
switch key {
186+
case "id":
187+
if index.Id, err = d.DecodeUint32(); err != nil {
188+
return err
189+
}
190+
case "name":
191+
if index.Name, err = d.DecodeString(); err != nil {
192+
return err
193+
}
194+
case "type":
195+
if index.Type, err = d.DecodeString(); err != nil {
196+
return err
197+
}
198+
case "unique":
199+
if index.Unique, err = d.DecodeBool(); err != nil {
200+
return err
201+
}
202+
case "parts":
203+
la, err := d.DecodeArrayLen()
204+
if err != nil {
205+
return err
206+
}
207+
208+
index.Parts = make([]IndexPart, la)
209+
for j := 0; j < la; j++ {
210+
if err := d.Decode(&index.Parts[j]); err != nil {
211+
return err
212+
}
213+
}
214+
default:
215+
if err := d.Skip(); err != nil {
216+
return err
217+
}
218+
}
219+
}
220+
221+
return nil
222+
}
223+
224+
// IndexField contains a CRUD space index part definition.
225+
type IndexPart struct {
226+
Fieldno uint32
227+
Type string
228+
ExcludeNull bool
229+
IsNullable bool
230+
}
231+
232+
// DecodeMsgpack provides custom msgpack decoder.
233+
func (indexPart *IndexPart) DecodeMsgpack(d *msgpack.Decoder) error {
234+
l, err := d.DecodeMapLen()
235+
if err != nil {
236+
return err
237+
}
238+
for i := 0; i < l; i++ {
239+
key, err := d.DecodeString()
240+
if err != nil {
241+
return err
242+
}
243+
244+
switch key {
245+
case "fieldno":
246+
if indexPart.Fieldno, err = d.DecodeUint32(); err != nil {
247+
return err
248+
}
249+
case "type":
250+
if indexPart.Type, err = d.DecodeString(); err != nil {
251+
return err
252+
}
253+
case "exclude_null":
254+
if indexPart.ExcludeNull, err = d.DecodeBool(); err != nil {
255+
return err
256+
}
257+
case "is_nullable":
258+
if indexPart.IsNullable, err = d.DecodeBool(); err != nil {
259+
return err
260+
}
261+
default:
262+
if err := d.Skip(); err != nil {
263+
return err
264+
}
265+
}
266+
}
267+
268+
return nil
269+
}
270+
271+
// SchemaResult contains a schema request result for all spaces.
272+
type SchemaResult struct {
273+
Value Schema
274+
}
275+
276+
// DecodeMsgpack provides custom msgpack decoder.
277+
func (schemaResult *SchemaResult) DecodeMsgpack(d *msgpack.Decoder) error {
278+
arrLen, err := d.DecodeArrayLen()
279+
if err != nil {
280+
return err
281+
}
282+
283+
if arrLen == 0 {
284+
return fmt.Errorf("unexpected empty response array")
285+
}
286+
287+
// DecodeMapLen inside Schema decode processes `nil` as zero length map,
288+
// so in `return nil, err` case we don't miss error info.
289+
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81
290+
if err = d.Decode(&schemaResult.Value); err != nil {
291+
return err
292+
}
293+
294+
if arrLen > 1 {
295+
var crudErr *Error = nil
296+
297+
if err := d.Decode(&crudErr); err != nil {
298+
return err
299+
}
300+
301+
if crudErr != nil {
302+
return crudErr
303+
}
304+
}
305+
306+
for i := 2; i < arrLen; i++ {
307+
if err := d.Skip(); err != nil {
308+
return err
309+
}
310+
}
311+
312+
return nil
313+
}
314+
315+
// SchemaResult contains a schema request result for a single space.
316+
type SpaceSchemaResult struct {
317+
Value SpaceSchema
318+
}
319+
320+
// DecodeMsgpack provides custom msgpack decoder.
321+
func (spaceSchemaResult *SpaceSchemaResult) DecodeMsgpack(d *msgpack.Decoder) error {
322+
arrLen, err := d.DecodeArrayLen()
323+
if err != nil {
324+
return err
325+
}
326+
327+
if arrLen == 0 {
328+
return fmt.Errorf("unexpected empty response array")
329+
}
330+
331+
// DecodeMapLen inside SpaceSchema decode processes `nil` as zero length map,
332+
// so in `return nil, err` case we don't miss error info.
333+
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81
334+
if err = d.Decode(&spaceSchemaResult.Value); err != nil {
335+
return err
336+
}
337+
338+
if arrLen > 1 {
339+
var crudErr *Error = nil
340+
341+
if err := d.Decode(&crudErr); err != nil {
342+
return err
343+
}
344+
345+
if crudErr != nil {
346+
return crudErr
347+
}
348+
}
349+
350+
for i := 2; i < arrLen; i++ {
351+
if err := d.Skip(); err != nil {
352+
return err
353+
}
354+
}
355+
356+
return nil
357+
}

0 commit comments

Comments
 (0)