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

tarantool / go-tarantool / 7656911175

25 Jan 2024 03:39PM UTC coverage: 74.243% (+0.05%) from 74.194%
7656911175

push

github

oleg-jukovec
pool: add Instance type

The type Instance:
```
type Instance struct {
	// Name is an unique name of the instance.
	Name   string
	// Dialer will be used to create a connection to the instance.
	Dialer tarantool.Dialer
	// Opts will be used to specify a connection options.
	Opts   tarantool.Opts
}
```

The type allows to specify a dialer and connection options per a
pool instance. It is used in `pool.Connect`, `pool.ConnectWithOpts`
and `pool.Add` to specify an instance configuration now.

Closes #356

85 of 96 new or added lines in 2 files covered. (88.54%)

180 existing lines in 4 files now uncovered.

6010 of 8095 relevant lines covered (74.24%)

10248.45 hits per line

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

56.57
/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 {
76,120✔
23
        return code == msgpcode.Uint8 || code == msgpcode.Uint16 ||
76,120✔
24
                code == msgpcode.Uint32 || code == msgpcode.Uint64 ||
76,120✔
25
                msgpcode.IsFixedNum(code)
76,120✔
26
}
76,120✔
27

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

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

37
func msgpackIsString(code byte) bool {
19,747✔
38
        return msgpcode.IsFixedString(code) || code == msgpcode.Str8 ||
19,747✔
39
                code == msgpcode.Str16 || code == msgpcode.Str32
19,747✔
40
}
19,747✔
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 {
526✔
66
        schemaCopy := *schema
526✔
67
        schemaCopy.Spaces = make(map[string]Space, len(schema.Spaces))
526✔
68
        for name, space := range schema.Spaces {
17,938✔
69
                schemaCopy.Spaces[name] = space.copy()
17,412✔
70
        }
17,412✔
71
        schemaCopy.SpacesById = make(map[uint32]Space, len(schema.SpacesById))
526✔
72
        for id, space := range schema.SpacesById {
17,938✔
73
                schemaCopy.SpacesById[id] = space.copy()
17,412✔
74
        }
17,412✔
75
        return schemaCopy
526✔
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 {
34,824✔
96
        spaceCopy := *space
34,824✔
97
        spaceCopy.Fields = make(map[string]Field, len(space.Fields))
34,824✔
98
        for name, field := range space.Fields {
219,062✔
99
                spaceCopy.Fields[name] = field
184,238✔
100
        }
184,238✔
101
        spaceCopy.FieldsById = make(map[uint32]Field, len(space.FieldsById))
34,824✔
102
        for id, field := range space.FieldsById {
219,062✔
103
                spaceCopy.FieldsById[id] = field
184,238✔
104
        }
184,238✔
105
        spaceCopy.Indexes = make(map[string]Index, len(space.Indexes))
34,824✔
106
        for name, index := range space.Indexes {
101,990✔
107
                spaceCopy.Indexes[name] = index.copy()
67,166✔
108
        }
67,166✔
109
        spaceCopy.IndexesById = make(map[uint32]Index, len(space.IndexesById))
34,824✔
110
        for id, index := range space.IndexesById {
101,990✔
111
                spaceCopy.IndexesById[id] = index.copy()
67,166✔
112
        }
67,166✔
113
        return spaceCopy
34,824✔
114
}
115

116
func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error {
19,753✔
117
        arrayLen, err := d.DecodeArrayLen()
19,753✔
118
        if err != nil {
19,753✔
119
                return err
×
120
        }
×
121
        if space.Id, err = d.DecodeUint32(); err != nil {
19,753✔
122
                return err
×
123
        }
×
124
        if err := d.Skip(); err != nil {
19,753✔
125
                return err
×
126
        }
×
127
        if space.Name, err = d.DecodeString(); err != nil {
19,753✔
128
                return err
×
129
        }
×
130
        if space.Engine, err = d.DecodeString(); err != nil {
19,753✔
131
                return err
×
132
        }
×
133
        if space.FieldsCount, err = d.DecodeUint32(); err != nil {
19,753✔
134
                return err
×
135
        }
×
136
        if arrayLen >= vspaceSpTypeFieldNum {
39,500✔
137
                code, err := d.PeekCode()
19,747✔
138
                if err != nil {
19,747✔
139
                        return err
×
140
                }
×
141
                if msgpackIsString(code) {
19,747✔
142
                        val, err := d.DecodeString()
×
143
                        if err != nil {
×
144
                                return err
×
145
                        }
×
146
                        space.Temporary = val == "temporary"
×
147
                } else if msgpackIsMap(code) {
39,494✔
148
                        mapLen, err := d.DecodeMapLen()
19,747✔
149
                        if err != nil {
19,747✔
150
                                return err
×
151
                        }
×
152
                        for i := 0; i < mapLen; i++ {
22,714✔
153
                                key, err := d.DecodeString()
2,967✔
154
                                if err != nil {
2,967✔
155
                                        return err
×
156
                                }
×
157
                                if key == "temporary" {
5,341✔
158
                                        if space.Temporary, err = d.DecodeBool(); err != nil {
2,374✔
159
                                                return err
×
160
                                        }
×
161
                                } else {
593✔
162
                                        if err = d.Skip(); err != nil {
593✔
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)
19,753✔
172
        space.Fields = make(map[string]Field)
19,753✔
173
        space.IndexesById = make(map[uint32]Index)
19,753✔
174
        space.Indexes = make(map[string]Index)
19,753✔
175
        if arrayLen >= vspaceSpFormatFieldNum {
39,500✔
176
                fieldCount, err := d.DecodeArrayLen()
19,747✔
177
                if err != nil {
19,747✔
178
                        return err
×
179
                }
×
180
                for i := 0; i < fieldCount; i++ {
123,955✔
181
                        field := Field{}
104,208✔
182
                        if err := field.DecodeMsgpack(d); err != nil {
104,208✔
183
                                return err
×
184
                        }
×
185
                        field.Id = uint32(i)
104,208✔
186
                        space.FieldsById[field.Id] = field
104,208✔
187
                        if field.Name != "" {
208,416✔
188
                                space.Fields[field.Name] = field
104,208✔
189
                        }
104,208✔
190
                }
191
        }
192
        return nil
19,753✔
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 {
104,208✔
204
        l, err := d.DecodeMapLen()
104,208✔
205
        if err != nil {
104,208✔
206
                return err
×
207
        }
×
208
        for i := 0; i < l; i++ {
314,107✔
209
                key, err := d.DecodeString()
209,899✔
210
                if err != nil {
209,899✔
211
                        return err
×
212
                }
×
213
                switch key {
209,899✔
214
                case "name":
104,208✔
215
                        if field.Name, err = d.DecodeString(); err != nil {
104,208✔
216
                                return err
×
217
                        }
×
218
                case "type":
104,208✔
219
                        if field.Type, err = d.DecodeString(); err != nil {
104,208✔
220
                                return err
×
221
                        }
×
222
                case "is_nullable":
1,483✔
223
                        if field.IsNullable, err = d.DecodeBool(); err != nil {
1,483✔
224
                                return err
×
225
                        }
×
226
                default:
×
227
                        if err := d.Skip(); err != nil {
×
228
                                return err
×
229
                        }
×
230
                }
231
        }
232
        return nil
104,208✔
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 {
134,332✔
246
        indexCopy := *index
134,332✔
247
        indexCopy.Fields = make([]IndexField, len(index.Fields))
134,332✔
248
        copy(indexCopy.Fields, index.Fields)
134,332✔
249
        return indexCopy
134,332✔
250
}
134,332✔
251

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

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

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

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

288
                var ok bool
38,058✔
289
                if index.Unique, ok = optsMap["unique"].(bool); !ok {
38,058✔
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 {
38,060✔
296
                return err
×
297
        }
×
298

299
        if msgpackIsUint(code) {
38,062✔
300
                fieldCount, err := d.DecodeUint64()
2✔
301
                if err != nil {
2✔
302
                        return err
×
303
                }
×
304
                index.Fields = make([]IndexField, fieldCount)
2✔
305
                for i := 0; i < int(fieldCount); i++ {
2✔
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 {
38,058✔
315
                if err := d.Decode(&index.Fields); err != nil {
38,058✔
316
                        return fmt.Errorf("unexpected schema format (index flags): %w", err)
×
317
                }
×
318
        }
319

320
        return nil
38,060✔
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 {
46,963✔
330
        code, err := d.PeekCode()
46,963✔
331
        if err != nil {
46,963✔
332
                return err
×
333
        }
×
334

335
        if msgpackIsMap(code) {
46,963✔
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) {
93,926✔
362
                arrayLen, err := d.DecodeArrayLen()
46,963✔
363
                if err != nil {
46,963✔
364
                        return err
×
365
                }
×
366
                if indexField.Id, err = d.DecodeUint32(); err != nil {
46,963✔
367
                        return err
×
368
                }
×
369
                if indexField.Type, err = d.DecodeString(); err != nil {
46,963✔
370
                        return err
×
371
                }
×
372
                for i := 2; i < arrayLen; i++ {
46,963✔
373
                        if err := d.Skip(); err != nil {
×
374
                                return err
×
375
                        }
×
376
                }
377
                return nil
46,963✔
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) {
601✔
385
        schema := Schema{}
601✔
386
        schema.SpacesById = make(map[uint32]Space)
601✔
387
        schema.Spaces = make(map[string]Space)
601✔
388

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

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

422
        return schema, nil
538✔
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) {
1,350✔
428
        var spaceNo uint32
1,350✔
429

1,350✔
430
        switch s := s.(type) {
1,350✔
431
        case uint:
×
432
                spaceNo = uint32(s)
×
433
        case uint64:
×
434
                spaceNo = uint32(s)
×
435
        case uint32:
152✔
436
                spaceNo = s
152✔
437
        case uint16:
×
438
                spaceNo = uint32(s)
×
439
        case uint8:
×
440
                spaceNo = uint32(s)
×
441
        case int:
1,198✔
442
                spaceNo = uint32(s)
1,198✔
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
1,350✔
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) {
1,288✔
465
        var indexNo uint32
1,288✔
466

1,288✔
467
        switch i := i.(type) {
1,288✔
468
        case uint:
×
469
                indexNo = uint32(i)
×
470
        case uint64:
×
471
                indexNo = uint32(i)
×
472
        case uint32:
98✔
473
                indexNo = i
98✔
474
        case uint16:
×
475
                indexNo = uint32(i)
×
476
        case uint8:
×
477
                indexNo = uint32(i)
×
478
        case int:
1,190✔
479
                indexNo = uint32(i)
1,190✔
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
1,288✔
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) {
309✔
507
        if str, ok := s.(string); ok {
434✔
UNCOV
508
                space, ok := r.Schema.Spaces[str]
125✔
UNCOV
509
                if !ok {
125✔
510
                        return 0, fmt.Errorf("there is no space with name %s", s)
×
511
                }
×
UNCOV
512
                return space.Id, nil
125✔
513
        }
514
        return resolveSpaceNumber(s)
184✔
515
}
516

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

536
func (r *loadedSchemaResolver) NamesUseSupported() bool {
624✔
537
        return r.SpaceAndIndexNamesSupported
624✔
538
}
624✔
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) {
1,166✔
547
        if _, ok := s.(string); ok {
1,166✔
548
                return 0, fmt.Errorf("unable to use an index name " +
×
549
                        "because schema is not loaded")
×
550
        }
×
551
        return resolveSpaceNumber(s)
1,166✔
552
}
553

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

562
func (r *noSchemaResolver) NamesUseSupported() bool {
2,332✔
563
        return r.SpaceAndIndexNamesSupported
2,332✔
564
}
2,332✔
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

© 2025 Coveralls, Inc