• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / go-tarantool / 13930913017

18 Mar 2025 06:33PM UTC coverage: 75.939% (+0.08%) from 75.863%
13930913017

Pull #435

github

maksim.konovalov
pool: Pooler interface supports GetInfo method in TopologyEditor
Pull Request #435: pool: Pooler interface supports GetInfo method in TopologyEditor

2992 of 3940 relevant lines covered (75.94%)

9863.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.55
/schema.go
1
package tarantool
2

3
import (
4
        "errors"
5
        "fmt"
6

7
        "github.com/vmihailenco/msgpack/v5"
8
        "github.com/vmihailenco/msgpack/v5/msgpcode"
9
)
10

11
// nolint: varcheck,deadcode
12
const (
13
        maxSchemas             = 10000
14
        spaceSpId              = 280
15
        vspaceSpId             = 281
16
        indexSpId              = 288
17
        vindexSpId             = 289
18
        vspaceSpTypeFieldNum   = 6
19
        vspaceSpFormatFieldNum = 7
20
)
21

22
func msgpackIsUint(code byte) bool {
38,876✔
23
        return code == msgpcode.Uint8 || code == msgpcode.Uint16 ||
38,876✔
24
                code == msgpcode.Uint32 || code == msgpcode.Uint64 ||
38,876✔
25
                msgpcode.IsFixedNum(code)
38,876✔
26
}
38,876✔
27

28
func msgpackIsMap(code byte) bool {
34,132✔
29
        return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code)
34,132✔
30
}
34,132✔
31

32
func msgpackIsArray(code byte) bool {
23,912✔
33
        return code == msgpcode.Array16 || code == msgpcode.Array32 ||
23,912✔
34
                msgpcode.IsFixedArray(code)
23,912✔
35
}
23,912✔
36

37
func msgpackIsString(code byte) bool {
10,220✔
38
        return msgpcode.IsFixedString(code) || code == msgpcode.Str8 ||
10,220✔
39
                code == msgpcode.Str16 || code == msgpcode.Str32
10,220✔
40
}
10,220✔
41

42
// SchemaResolver is an interface for resolving schema details.
43
type SchemaResolver interface {
44
        // ResolveSpace returns resolved space number or an error
45
        // if it cannot be resolved.
46
        ResolveSpace(s interface{}) (spaceNo uint32, err error)
47
        // ResolveIndex returns resolved index number or an error
48
        // if it cannot be resolved.
49
        ResolveIndex(i interface{}, spaceNo uint32) (indexNo uint32, err error)
50
        // NamesUseSupported shows if usage of space and index names, instead of
51
        // IDs, is supported. It must return true if
52
        // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES is supported.
53
        NamesUseSupported() bool
54
}
55

56
// Schema contains information about spaces and indexes.
57
type Schema struct {
58
        Version uint
59
        // Spaces is map from space names to spaces.
60
        Spaces map[string]Space
61
        // SpacesById is map from space numbers to spaces.
62
        SpacesById map[uint32]Space
63
}
64

65
func (schema *Schema) copy() Schema {
251✔
66
        schemaCopy := *schema
251✔
67
        schemaCopy.Spaces = make(map[string]Space, len(schema.Spaces))
251✔
68
        for name, space := range schema.Spaces {
8,804✔
69
                schemaCopy.Spaces[name] = space.copy()
8,553✔
70
        }
8,553✔
71
        schemaCopy.SpacesById = make(map[uint32]Space, len(schema.SpacesById))
251✔
72
        for id, space := range schema.SpacesById {
8,804✔
73
                schemaCopy.SpacesById[id] = space.copy()
8,553✔
74
        }
8,553✔
75
        return schemaCopy
251✔
76
}
77

78
// Space contains information about Tarantool's space.
79
type Space struct {
80
        Id   uint32
81
        Name string
82
        // Could be "memtx" or "vinyl".
83
        Engine    string
84
        Temporary bool // Is this space temporary?
85
        // Field configuration is not mandatory and not checked by Tarantool.
86
        FieldsCount uint32
87
        Fields      map[string]Field
88
        FieldsById  map[uint32]Field
89
        // Indexes is map from index names to indexes.
90
        Indexes map[string]Index
91
        // IndexesById is map from index numbers to indexes.
92
        IndexesById map[uint32]Index
93
}
94

95
func (space *Space) copy() Space {
17,106✔
96
        spaceCopy := *space
17,106✔
97
        spaceCopy.Fields = make(map[string]Field, len(space.Fields))
17,106✔
98
        for name, field := range space.Fields {
107,744✔
99
                spaceCopy.Fields[name] = field
90,638✔
100
        }
90,638✔
101
        spaceCopy.FieldsById = make(map[uint32]Field, len(space.FieldsById))
17,106✔
102
        for id, field := range space.FieldsById {
107,744✔
103
                spaceCopy.FieldsById[id] = field
90,638✔
104
        }
90,638✔
105
        spaceCopy.Indexes = make(map[string]Index, len(space.Indexes))
17,106✔
106
        for name, index := range space.Indexes {
49,654✔
107
                spaceCopy.Indexes[name] = index.copy()
32,548✔
108
        }
32,548✔
109
        spaceCopy.IndexesById = make(map[uint32]Index, len(space.IndexesById))
17,106✔
110
        for id, index := range space.IndexesById {
49,654✔
111
                spaceCopy.IndexesById[id] = index.copy()
32,548✔
112
        }
32,548✔
113
        return spaceCopy
17,106✔
114
}
115

116
func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error {
10,223✔
117
        arrayLen, err := d.DecodeArrayLen()
10,223✔
118
        if err != nil {
10,223✔
119
                return err
×
120
        }
×
121
        if space.Id, err = d.DecodeUint32(); err != nil {
10,223✔
122
                return err
×
123
        }
×
124
        if err := d.Skip(); err != nil {
10,223✔
125
                return err
×
126
        }
×
127
        if space.Name, err = d.DecodeString(); err != nil {
10,223✔
128
                return err
×
129
        }
×
130
        if space.Engine, err = d.DecodeString(); err != nil {
10,223✔
131
                return err
×
132
        }
×
133
        if space.FieldsCount, err = d.DecodeUint32(); err != nil {
10,223✔
134
                return err
×
135
        }
×
136
        if arrayLen >= vspaceSpTypeFieldNum {
20,443✔
137
                code, err := d.PeekCode()
10,220✔
138
                if err != nil {
10,220✔
139
                        return err
×
140
                }
×
141
                if msgpackIsString(code) {
10,220✔
142
                        val, err := d.DecodeString()
×
143
                        if err != nil {
×
144
                                return err
×
145
                        }
×
146
                        space.Temporary = val == "temporary"
×
147
                } else if msgpackIsMap(code) {
20,440✔
148
                        mapLen, err := d.DecodeMapLen()
10,220✔
149
                        if err != nil {
10,220✔
150
                                return err
×
151
                        }
×
152
                        for i := 0; i < mapLen; i++ {
12,009✔
153
                                key, err := d.DecodeString()
1,789✔
154
                                if err != nil {
1,789✔
155
                                        return err
×
156
                                }
×
157
                                if key == "temporary" {
2,982✔
158
                                        if space.Temporary, err = d.DecodeBool(); err != nil {
1,193✔
159
                                                return err
×
160
                                        }
×
161
                                } else {
596✔
162
                                        if err = d.Skip(); err != nil {
596✔
163
                                                return err
×
164
                                        }
×
165
                                }
166
                        }
167
                } else {
×
168
                        return errors.New("unexpected schema format (space flags)")
×
169
                }
×
170
        }
171
        space.FieldsById = make(map[uint32]Field)
10,223✔
172
        space.Fields = make(map[string]Field)
10,223✔
173
        space.IndexesById = make(map[uint32]Index)
10,223✔
174
        space.Indexes = make(map[string]Index)
10,223✔
175
        if arrayLen >= vspaceSpFormatFieldNum {
20,443✔
176
                fieldCount, err := d.DecodeArrayLen()
10,220✔
177
                if err != nil {
10,220✔
178
                        return err
×
179
                }
×
180
                for i := 0; i < fieldCount; i++ {
64,227✔
181
                        field := Field{}
54,007✔
182
                        if err := field.DecodeMsgpack(d); err != nil {
54,007✔
183
                                return err
×
184
                        }
×
185
                        field.Id = uint32(i)
54,007✔
186
                        space.FieldsById[field.Id] = field
54,007✔
187
                        if field.Name != "" {
108,014✔
188
                                space.Fields[field.Name] = field
54,007✔
189
                        }
54,007✔
190
                }
191
        }
192
        return nil
10,223✔
193
}
194

195
// Field is a schema field.
196
type Field struct {
197
        Id         uint32
198
        Name       string
199
        Type       string
200
        IsNullable bool
201
}
202

203
func (field *Field) DecodeMsgpack(d *msgpack.Decoder) error {
54,007✔
204
        l, err := d.DecodeMapLen()
54,007✔
205
        if err != nil {
54,007✔
206
                return err
×
207
        }
×
208
        for i := 0; i < l; i++ {
162,915✔
209
                key, err := d.DecodeString()
108,908✔
210
                if err != nil {
108,908✔
211
                        return err
×
212
                }
×
213
                switch key {
108,908✔
214
                case "name":
54,007✔
215
                        if field.Name, err = d.DecodeString(); err != nil {
54,007✔
216
                                return err
×
217
                        }
×
218
                case "type":
54,007✔
219
                        if field.Type, err = d.DecodeString(); err != nil {
54,007✔
220
                                return err
×
221
                        }
×
222
                case "is_nullable":
894✔
223
                        if field.IsNullable, err = d.DecodeBool(); err != nil {
894✔
224
                                return err
×
225
                        }
×
226
                default:
×
227
                        if err := d.Skip(); err != nil {
×
228
                                return err
×
229
                        }
×
230
                }
231
        }
232
        return nil
54,007✔
233
}
234

235
// Index contains information about index.
236
type Index struct {
237
        Id      uint32
238
        SpaceId uint32
239
        Name    string
240
        Type    string
241
        Unique  bool
242
        Fields  []IndexField
243
}
244

245
func (index *Index) copy() Index {
65,096✔
246
        indexCopy := *index
65,096✔
247
        indexCopy.Fields = make([]IndexField, len(index.Fields))
65,096✔
248
        copy(indexCopy.Fields, index.Fields)
65,096✔
249
        return indexCopy
65,096✔
250
}
65,096✔
251

252
func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error {
19,438✔
253
        _, err := d.DecodeArrayLen()
19,438✔
254
        if err != nil {
19,438✔
255
                return err
×
256
        }
×
257

258
        if index.SpaceId, err = d.DecodeUint32(); err != nil {
19,438✔
259
                return err
×
260
        }
×
261
        if index.Id, err = d.DecodeUint32(); err != nil {
19,438✔
262
                return err
×
263
        }
×
264
        if index.Name, err = d.DecodeString(); err != nil {
19,438✔
265
                return err
×
266
        }
×
267
        if index.Type, err = d.DecodeString(); err != nil {
19,438✔
268
                return err
×
269
        }
×
270

271
        var code byte
19,438✔
272
        if code, err = d.PeekCode(); err != nil {
19,438✔
273
                return err
×
274
        }
×
275

276
        if msgpackIsUint(code) {
19,439✔
277
                optsUint64, err := d.DecodeUint64()
1✔
278
                if err != nil {
1✔
279
                        return nil
×
280
                }
×
281
                index.Unique = optsUint64 > 0
1✔
282
        } else {
19,437✔
283
                var optsMap map[string]interface{}
19,437✔
284
                if err := d.Decode(&optsMap); err != nil {
19,437✔
285
                        return fmt.Errorf("unexpected schema format (index flags): %w", err)
×
286
                }
×
287

288
                var ok bool
19,437✔
289
                if index.Unique, ok = optsMap["unique"].(bool); !ok {
19,437✔
290
                        /* see bug https://github.com/tarantool/tarantool/issues/2060 */
×
291
                        index.Unique = true
×
292
                }
×
293
        }
294

295
        if code, err = d.PeekCode(); err != nil {
19,438✔
296
                return err
×
297
        }
×
298

299
        if msgpackIsUint(code) {
19,439✔
300
                fieldCount, err := d.DecodeUint64()
1✔
301
                if err != nil {
1✔
302
                        return err
×
303
                }
×
304
                index.Fields = make([]IndexField, fieldCount)
1✔
305
                for i := 0; i < int(fieldCount); i++ {
1✔
306
                        index.Fields[i] = IndexField{}
×
307
                        if index.Fields[i].Id, err = d.DecodeUint32(); err != nil {
×
308
                                return err
×
309
                        }
×
310
                        if index.Fields[i].Type, err = d.DecodeString(); err != nil {
×
311
                                return err
×
312
                        }
×
313
                }
314
        } else {
19,437✔
315
                if err := d.Decode(&index.Fields); err != nil {
19,437✔
316
                        return fmt.Errorf("unexpected schema format (index flags): %w", err)
×
317
                }
×
318
        }
319

320
        return nil
19,438✔
321
}
322

323
// IndexFields is an index field.
324
type IndexField struct {
325
        Id   uint32
326
        Type string
327
}
328

329
func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error {
23,912✔
330
        code, err := d.PeekCode()
23,912✔
331
        if err != nil {
23,912✔
332
                return err
×
333
        }
×
334

335
        if msgpackIsMap(code) {
23,912✔
336
                mapLen, err := d.DecodeMapLen()
×
337
                if err != nil {
×
338
                        return err
×
339
                }
×
340
                for i := 0; i < mapLen; i++ {
×
341
                        key, err := d.DecodeString()
×
342
                        if err != nil {
×
343
                                return err
×
344
                        }
×
345
                        switch key {
×
346
                        case "field":
×
347
                                if indexField.Id, err = d.DecodeUint32(); err != nil {
×
348
                                        return err
×
349
                                }
×
350
                        case "type":
×
351
                                if indexField.Type, err = d.DecodeString(); err != nil {
×
352
                                        return err
×
353
                                }
×
354
                        default:
×
355
                                if err := d.Skip(); err != nil {
×
356
                                        return err
×
357
                                }
×
358
                        }
359
                }
360
                return nil
×
361
        } else if msgpackIsArray(code) {
47,824✔
362
                arrayLen, err := d.DecodeArrayLen()
23,912✔
363
                if err != nil {
23,912✔
364
                        return err
×
365
                }
×
366
                if indexField.Id, err = d.DecodeUint32(); err != nil {
23,912✔
367
                        return err
×
368
                }
×
369
                if indexField.Type, err = d.DecodeString(); err != nil {
23,912✔
370
                        return err
×
371
                }
×
372
                for i := 2; i < arrayLen; i++ {
23,912✔
373
                        if err := d.Skip(); err != nil {
×
374
                                return err
×
375
                        }
×
376
                }
377
                return nil
23,912✔
378
        }
379

380
        return errors.New("unexpected schema format (index fields)")
×
381
}
382

383
// GetSchema returns the actual schema for the Doer.
384
func GetSchema(doer Doer) (Schema, error) {
302✔
385
        schema := Schema{}
302✔
386
        schema.SpacesById = make(map[uint32]Space)
302✔
387
        schema.Spaces = make(map[string]Space)
302✔
388

302✔
389
        // Reload spaces.
302✔
390
        var spaces []Space
302✔
391
        req := NewSelectRequest(vspaceSpId).
302✔
392
                Index(0).
302✔
393
                Limit(maxSchemas)
302✔
394
        err := doer.Do(req).GetTyped(&spaces)
302✔
395
        if err != nil {
303✔
396
                return Schema{}, err
1✔
397
        }
1✔
398
        for _, space := range spaces {
10,524✔
399
                schema.SpacesById[space.Id] = space
10,223✔
400
                schema.Spaces[space.Name] = space
10,223✔
401
        }
10,223✔
402

403
        // Reload indexes.
404
        var indexes []Index
301✔
405
        req = NewSelectRequest(vindexSpId).
301✔
406
                Index(0).
301✔
407
                Limit(maxSchemas)
301✔
408
        err = doer.Do(req).GetTyped(&indexes)
301✔
409
        if err != nil {
302✔
410
                return Schema{}, err
1✔
411
        }
1✔
412
        for _, index := range indexes {
19,738✔
413
                spaceId := index.SpaceId
19,438✔
414
                if _, ok := schema.SpacesById[spaceId]; ok {
38,833✔
415
                        schema.SpacesById[spaceId].IndexesById[index.Id] = index
19,395✔
416
                        schema.SpacesById[spaceId].Indexes[index.Name] = index
19,395✔
417
                } else {
19,438✔
418
                        return Schema{}, errors.New("concurrent schema update")
43✔
419
                }
43✔
420
        }
421

422
        return schema, nil
257✔
423
}
424

425
// resolveSpaceNumber tries to resolve a space number.
426
// Note: at this point, s can be a number, or an object of Space type.
427
func resolveSpaceNumber(s interface{}) (uint32, error) {
678✔
428
        var spaceNo uint32
678✔
429

678✔
430
        switch s := s.(type) {
678✔
431
        case uint:
×
432
                spaceNo = uint32(s)
×
433
        case uint64:
×
434
                spaceNo = uint32(s)
×
435
        case uint32:
76✔
436
                spaceNo = s
76✔
437
        case uint16:
×
438
                spaceNo = uint32(s)
×
439
        case uint8:
×
440
                spaceNo = uint32(s)
×
441
        case int:
602✔
442
                spaceNo = uint32(s)
602✔
443
        case int64:
×
444
                spaceNo = uint32(s)
×
445
        case int32:
×
446
                spaceNo = uint32(s)
×
447
        case int16:
×
448
                spaceNo = uint32(s)
×
449
        case int8:
×
450
                spaceNo = uint32(s)
×
451
        case Space:
×
452
                spaceNo = s.Id
×
453
        case *Space:
×
454
                spaceNo = s.Id
×
455
        default:
×
456
                panic("unexpected type of space param")
×
457
        }
458

459
        return spaceNo, nil
678✔
460
}
461

462
// resolveIndexNumber tries to resolve an index number.
463
// Note: at this point, i can be a number, or an object of Index type.
464
func resolveIndexNumber(i interface{}) (uint32, error) {
647✔
465
        var indexNo uint32
647✔
466

647✔
467
        switch i := i.(type) {
647✔
468
        case uint:
×
469
                indexNo = uint32(i)
×
470
        case uint64:
×
471
                indexNo = uint32(i)
×
472
        case uint32:
49✔
473
                indexNo = i
49✔
474
        case uint16:
×
475
                indexNo = uint32(i)
×
476
        case uint8:
×
477
                indexNo = uint32(i)
×
478
        case int:
598✔
479
                indexNo = uint32(i)
598✔
480
        case int64:
×
481
                indexNo = uint32(i)
×
482
        case int32:
×
483
                indexNo = uint32(i)
×
484
        case int16:
×
485
                indexNo = uint32(i)
×
486
        case int8:
×
487
                indexNo = uint32(i)
×
488
        case Index:
×
489
                indexNo = i.Id
×
490
        case *Index:
×
491
                indexNo = i.Id
×
492
        default:
×
493
                panic("unexpected type of index param")
×
494
        }
495

496
        return indexNo, nil
647✔
497
}
498

499
type loadedSchemaResolver struct {
500
        Schema Schema
501
        // SpaceAndIndexNamesSupported shows if a current Tarantool version supports
502
        // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES.
503
        SpaceAndIndexNamesSupported bool
504
}
505

506
func (r *loadedSchemaResolver) ResolveSpace(s interface{}) (uint32, error) {
92✔
507
        if str, ok := s.(string); ok {
92✔
508
                space, ok := r.Schema.Spaces[str]
×
509
                if !ok {
×
510
                        return 0, fmt.Errorf("there is no space with name %s", s)
×
511
                }
×
512
                return space.Id, nil
×
513
        }
514
        return resolveSpaceNumber(s)
92✔
515
}
516

517
func (r *loadedSchemaResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) {
76✔
518
        if i == nil {
91✔
519
                return 0, nil
15✔
520
        }
15✔
521
        if str, ok := i.(string); ok {
61✔
522
                space, ok := r.Schema.SpacesById[spaceNo]
×
523
                if !ok {
×
524
                        return 0, fmt.Errorf("there is no space with id %d", spaceNo)
×
525
                }
×
526
                index, ok := space.Indexes[str]
×
527
                if !ok {
×
528
                        err := fmt.Errorf("space %s has not index with name %s", space.Name, i)
×
529
                        return 0, err
×
530
                }
×
531
                return index.Id, nil
×
532
        }
533
        return resolveIndexNumber(i)
61✔
534
}
535

536
func (r *loadedSchemaResolver) NamesUseSupported() bool {
314✔
537
        return r.SpaceAndIndexNamesSupported
314✔
538
}
314✔
539

540
type noSchemaResolver struct {
541
        // SpaceAndIndexNamesSupported shows if a current Tarantool version supports
542
        // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES.
543
        SpaceAndIndexNamesSupported bool
544
}
545

546
func (*noSchemaResolver) ResolveSpace(s interface{}) (uint32, error) {
586✔
547
        if _, ok := s.(string); ok {
586✔
548
                return 0, fmt.Errorf("unable to use an index name " +
×
549
                        "because schema is not loaded")
×
550
        }
×
551
        return resolveSpaceNumber(s)
586✔
552
}
553

554
func (*noSchemaResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) {
586✔
555
        if _, ok := i.(string); ok {
586✔
556
                return 0, fmt.Errorf("unable to use an index name " +
×
557
                        "because schema is not loaded")
×
558
        }
×
559
        return resolveIndexNumber(i)
586✔
560
}
561

562
func (r *noSchemaResolver) NamesUseSupported() bool {
1,172✔
563
        return r.SpaceAndIndexNamesSupported
1,172✔
564
}
1,172✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc