Skip to content

Commit ddd4f53

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

File tree

4 files changed

+468
-1
lines changed

4 files changed

+468
-1
lines changed

CHANGELOG.md

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

+1-1
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) # Will be replaced with 1.4.0 after release
3131

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

crud/schema.go

+360
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
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 err != nil {
72+
return err
73+
}
74+
if l != 0 {
75+
return fmt.Errorf("expected map or empty array, got non-empty array")
76+
}
77+
} else if msgpackIsMap(code) {
78+
l, err := d.DecodeMapLen()
79+
if err != nil {
80+
return err
81+
}
82+
*schema = make(map[string]SpaceSchema, l)
83+
84+
for i := 0; i < l; i++ {
85+
key, err := d.DecodeString()
86+
if err != nil {
87+
return err
88+
}
89+
90+
var spaceSchema SpaceSchema
91+
if err := d.Decode(&spaceSchema); err != nil {
92+
return err
93+
}
94+
95+
(*schema)[key] = spaceSchema
96+
}
97+
} else {
98+
return fmt.Errorf("unexpected code=%d decoding map or empty array", code)
99+
}
100+
101+
return nil
102+
}
103+
104+
// SpaceSchema contains a single CRUD space schema definition.
105+
type SpaceSchema struct {
106+
Format []FieldFormat
107+
Indexes map[uint32]Index
108+
}
109+
110+
// DecodeMsgpack provides custom msgpack decoder.
111+
func (spaceSchema *SpaceSchema) DecodeMsgpack(d *msgpack.Decoder) error {
112+
l, err := d.DecodeMapLen()
113+
if err != nil {
114+
return err
115+
}
116+
117+
for i := 0; i < l; i++ {
118+
key, err := d.DecodeString()
119+
if err != nil {
120+
return err
121+
}
122+
123+
switch key {
124+
case "format":
125+
la, err := d.DecodeArrayLen()
126+
if err != nil {
127+
return err
128+
}
129+
130+
spaceSchema.Format = make([]FieldFormat, la)
131+
for j := 0; j < la; j++ {
132+
if err := d.Decode(&spaceSchema.Format[j]); err != nil {
133+
return err
134+
}
135+
}
136+
case "indexes":
137+
lm, err := d.DecodeMapLen()
138+
if err != nil {
139+
return err
140+
}
141+
142+
spaceSchema.Indexes = make(map[uint32]Index, lm)
143+
for j := 0; j < lm; j++ {
144+
indexId, err := d.DecodeUint32()
145+
if err != nil {
146+
return err
147+
}
148+
149+
var indexObj Index
150+
if err := d.Decode(&indexObj); err != nil {
151+
return err
152+
}
153+
154+
spaceSchema.Indexes[indexId] = indexObj
155+
}
156+
default:
157+
if err := d.Skip(); err != nil {
158+
return err
159+
}
160+
}
161+
}
162+
163+
return nil
164+
}
165+
166+
// Index contains a CRUD space index definition.
167+
type Index struct {
168+
Id uint32
169+
Name string
170+
Type string
171+
Unique bool
172+
Parts []IndexPart
173+
}
174+
175+
// DecodeMsgpack provides custom msgpack decoder.
176+
func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error {
177+
l, err := d.DecodeMapLen()
178+
if err != nil {
179+
return err
180+
}
181+
182+
for i := 0; i < l; i++ {
183+
key, err := d.DecodeString()
184+
if err != nil {
185+
return err
186+
}
187+
188+
switch key {
189+
case "id":
190+
if index.Id, err = d.DecodeUint32(); err != nil {
191+
return err
192+
}
193+
case "name":
194+
if index.Name, err = d.DecodeString(); err != nil {
195+
return err
196+
}
197+
case "type":
198+
if index.Type, err = d.DecodeString(); err != nil {
199+
return err
200+
}
201+
case "unique":
202+
if index.Unique, err = d.DecodeBool(); err != nil {
203+
return err
204+
}
205+
case "parts":
206+
la, err := d.DecodeArrayLen()
207+
if err != nil {
208+
return err
209+
}
210+
211+
index.Parts = make([]IndexPart, la)
212+
for j := 0; j < la; j++ {
213+
if err := d.Decode(&index.Parts[j]); err != nil {
214+
return err
215+
}
216+
}
217+
default:
218+
if err := d.Skip(); err != nil {
219+
return err
220+
}
221+
}
222+
}
223+
224+
return nil
225+
}
226+
227+
// IndexField contains a CRUD space index part definition.
228+
type IndexPart struct {
229+
Fieldno uint32
230+
Type string
231+
ExcludeNull bool
232+
IsNullable bool
233+
}
234+
235+
// DecodeMsgpack provides custom msgpack decoder.
236+
func (indexPart *IndexPart) DecodeMsgpack(d *msgpack.Decoder) error {
237+
l, err := d.DecodeMapLen()
238+
if err != nil {
239+
return err
240+
}
241+
for i := 0; i < l; i++ {
242+
key, err := d.DecodeString()
243+
if err != nil {
244+
return err
245+
}
246+
247+
switch key {
248+
case "fieldno":
249+
if indexPart.Fieldno, err = d.DecodeUint32(); err != nil {
250+
return err
251+
}
252+
case "type":
253+
if indexPart.Type, err = d.DecodeString(); err != nil {
254+
return err
255+
}
256+
case "exclude_null":
257+
if indexPart.ExcludeNull, err = d.DecodeBool(); err != nil {
258+
return err
259+
}
260+
case "is_nullable":
261+
if indexPart.IsNullable, err = d.DecodeBool(); err != nil {
262+
return err
263+
}
264+
default:
265+
if err := d.Skip(); err != nil {
266+
return err
267+
}
268+
}
269+
}
270+
271+
return nil
272+
}
273+
274+
// SchemaResult contains a schema request result for all spaces.
275+
type SchemaResult struct {
276+
Value Schema
277+
}
278+
279+
// DecodeMsgpack provides custom msgpack decoder.
280+
func (schemaResult *SchemaResult) DecodeMsgpack(d *msgpack.Decoder) error {
281+
arrLen, err := d.DecodeArrayLen()
282+
if err != nil {
283+
return err
284+
}
285+
286+
if arrLen == 0 {
287+
return fmt.Errorf("unexpected empty response array")
288+
}
289+
290+
// DecodeMapLen inside Schema decode processes `nil` as zero length map,
291+
// so in `return nil, err` case we don't miss error info.
292+
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81
293+
if err = d.Decode(&schemaResult.Value); err != nil {
294+
return err
295+
}
296+
297+
if arrLen > 1 {
298+
var crudErr *Error = nil
299+
300+
if err := d.Decode(&crudErr); err != nil {
301+
return err
302+
}
303+
304+
if crudErr != nil {
305+
return crudErr
306+
}
307+
}
308+
309+
for i := 2; i < arrLen; i++ {
310+
if err := d.Skip(); err != nil {
311+
return err
312+
}
313+
}
314+
315+
return nil
316+
}
317+
318+
// SchemaResult contains a schema request result for a single space.
319+
type SpaceSchemaResult struct {
320+
Value SpaceSchema
321+
}
322+
323+
// DecodeMsgpack provides custom msgpack decoder.
324+
func (spaceSchemaResult *SpaceSchemaResult) DecodeMsgpack(d *msgpack.Decoder) error {
325+
arrLen, err := d.DecodeArrayLen()
326+
if err != nil {
327+
return err
328+
}
329+
330+
if arrLen == 0 {
331+
return fmt.Errorf("unexpected empty response array")
332+
}
333+
334+
// DecodeMapLen inside SpaceSchema decode processes `nil` as zero length map,
335+
// so in `return nil, err` case we don't miss error info.
336+
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81
337+
if err = d.Decode(&spaceSchemaResult.Value); err != nil {
338+
return err
339+
}
340+
341+
if arrLen > 1 {
342+
var crudErr *Error = nil
343+
344+
if err := d.Decode(&crudErr); err != nil {
345+
return err
346+
}
347+
348+
if crudErr != nil {
349+
return crudErr
350+
}
351+
}
352+
353+
for i := 2; i < arrLen; i++ {
354+
if err := d.Skip(); err != nil {
355+
return err
356+
}
357+
}
358+
359+
return nil
360+
}

0 commit comments

Comments
 (0)