Skip to content

Commit f6fc3d3

Browse files
committed
api: add pagination support
A user could fetch a position of a last tuple using a new method of the SelectRequest type: selectRequest = selectRequest.FetchPos(true) The position will be stored in a new field of the Response type: Response.Pos A user could specify a tuple from which selection must continue or its position with a new method of the SelectRequest type: selectRequest = selectRequest.After([]interface{}{23}) or selectRequest = selectRequest.After(resp.Pos) In action it looks like: req := NewSelectRequest(space).Key(key).Limit(10).FetchPos(true) for condition { resp, err := conn.Do(req).Get() // ... req = req.After(resp.Pos) } 1. tarantool/tarantool#7639 Part of #246
1 parent c3242e1 commit f6fc3d3

10 files changed

+406
-73
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1010

1111
### Added
1212

13+
- Support pagination (#246)
14+
1315
### Changed
1416

1517
### Fixed

const.go

+4
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,20 @@ const (
3030
KeyLimit = 0x12
3131
KeyOffset = 0x13
3232
KeyIterator = 0x14
33+
KeyFetchPos = 0x1f
3334
KeyKey = 0x20
3435
KeyTuple = 0x21
3536
KeyFunctionName = 0x22
3637
KeyUserName = 0x23
3738
KeyExpression = 0x27
39+
KeyAfterPos = 0x2e
40+
KeyAfterTuple = 0x2f
3841
KeyDefTuple = 0x28
3942
KeyData = 0x30
4043
KeyError24 = 0x31 /* Error in pre-2.4 format. */
4144
KeyMetaData = 0x32
4245
KeyBindCount = 0x34
46+
KeyPos = 0x35
4347
KeySQLText = 0x40
4448
KeySQLBind = 0x41
4549
KeySQLInfo = 0x42

example_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ func ExampleProtocolVersion() {
329329
fmt.Println("Connector client protocol features:", clientProtocolInfo.Features)
330330
// Output:
331331
// Connector client protocol version: 4
332-
// Connector client protocol features: [StreamsFeature TransactionsFeature ErrorExtensionFeature WatchersFeature]
332+
// Connector client protocol features: [StreamsFeature TransactionsFeature ErrorExtensionFeature WatchersFeature PaginationFeature]
333333
}
334334

335335
func getTestTxnOpts() tarantool.Opts {

export_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ func RefImplPingBody(enc *encoder) error {
2323

2424
// RefImplSelectBody is reference implementation for filling of a select
2525
// request's body.
26-
func RefImplSelectBody(enc *encoder, space, index, offset, limit, iterator uint32, key interface{}) error {
27-
return fillSelect(enc, space, index, offset, limit, iterator, key)
26+
func RefImplSelectBody(enc *encoder, space, index, offset, limit, iterator uint32,
27+
key, after interface{}, fetchPos bool) error {
28+
return fillSelect(enc, space, index, offset, limit, iterator, key, after, fetchPos)
2829
}
2930

3031
// RefImplInsertBody is reference implementation for filling of an insert

protocol.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const (
4747
// (supported by connector).
4848
WatchersFeature ProtocolFeature = 3
4949
// PaginationFeature represents support of pagination
50-
// (unsupported by connector).
50+
// (supported by connector).
5151
PaginationFeature ProtocolFeature = 4
5252
)
5353

@@ -83,11 +83,14 @@ var clientProtocolInfo ProtocolInfo = ProtocolInfo{
8383
// version 2 (Tarantool 2.10.0), in connector since 1.10.0.
8484
// Watchers were introduced in protocol version 3 (Tarantool 2.10.0), in
8585
// connector since 1.10.0.
86+
// Pagination were introduced in protocol version 4 (Tarantool 2.11.0), in
87+
// connector since 1.11.0.
8688
Features: []ProtocolFeature{
8789
StreamsFeature,
8890
TransactionsFeature,
8991
ErrorExtensionFeature,
9092
WatchersFeature,
93+
PaginationFeature,
9194
},
9295
}
9396

request.go

+118-23
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,103 @@ import (
1010
)
1111

1212
func fillSearch(enc *encoder, spaceNo, indexNo uint32, key interface{}) error {
13-
encodeUint(enc, KeySpaceNo)
14-
encodeUint(enc, uint64(spaceNo))
15-
encodeUint(enc, KeyIndexNo)
16-
encodeUint(enc, uint64(indexNo))
17-
encodeUint(enc, KeyKey)
13+
if err := encodeUint(enc, KeySpaceNo); err != nil {
14+
return err
15+
}
16+
if err := encodeUint(enc, uint64(spaceNo)); err != nil {
17+
return err
18+
}
19+
if err := encodeUint(enc, KeyIndexNo); err != nil {
20+
return err
21+
}
22+
if err := encodeUint(enc, uint64(indexNo)); err != nil {
23+
return err
24+
}
25+
if err := encodeUint(enc, KeyKey); err != nil {
26+
return err
27+
}
1828
return enc.Encode(key)
1929
}
2030

21-
func fillIterator(enc *encoder, offset, limit, iterator uint32) {
22-
encodeUint(enc, KeyIterator)
23-
encodeUint(enc, uint64(iterator))
24-
encodeUint(enc, KeyOffset)
25-
encodeUint(enc, uint64(offset))
26-
encodeUint(enc, KeyLimit)
27-
encodeUint(enc, uint64(limit))
31+
func fillIterator(enc *encoder, offset, limit, iterator uint32) error {
32+
if err := encodeUint(enc, KeyIterator); err != nil {
33+
return err
34+
}
35+
if err := encodeUint(enc, uint64(iterator)); err != nil {
36+
return err
37+
}
38+
if err := encodeUint(enc, KeyOffset); err != nil {
39+
return err
40+
}
41+
if err := encodeUint(enc, uint64(offset)); err != nil {
42+
return err
43+
}
44+
if err := encodeUint(enc, KeyLimit); err != nil {
45+
return err
46+
}
47+
return encodeUint(enc, uint64(limit))
2848
}
2949

3050
func fillInsert(enc *encoder, spaceNo uint32, tuple interface{}) error {
31-
enc.EncodeMapLen(2)
32-
encodeUint(enc, KeySpaceNo)
33-
encodeUint(enc, uint64(spaceNo))
34-
encodeUint(enc, KeyTuple)
51+
if err := enc.EncodeMapLen(2); err != nil {
52+
return err
53+
}
54+
if err := encodeUint(enc, KeySpaceNo); err != nil {
55+
return err
56+
}
57+
if err := encodeUint(enc, uint64(spaceNo)); err != nil {
58+
return err
59+
}
60+
if err := encodeUint(enc, KeyTuple); err != nil {
61+
return err
62+
}
3563
return enc.Encode(tuple)
3664
}
3765

38-
func fillSelect(enc *encoder, spaceNo, indexNo, offset, limit, iterator uint32, key interface{}) error {
39-
enc.EncodeMapLen(6)
40-
fillIterator(enc, offset, limit, iterator)
41-
return fillSearch(enc, spaceNo, indexNo, key)
66+
func fillSelect(enc *encoder, spaceNo, indexNo, offset, limit, iterator uint32,
67+
key, after interface{}, fetchPos bool) error {
68+
mapLen := 6
69+
if fetchPos {
70+
mapLen += 1
71+
}
72+
if after != nil {
73+
mapLen += 1
74+
}
75+
if err := enc.EncodeMapLen(mapLen); err != nil {
76+
return err
77+
}
78+
if err := fillIterator(enc, offset, limit, iterator); err != nil {
79+
return err
80+
}
81+
if err := fillSearch(enc, spaceNo, indexNo, key); err != nil {
82+
return err
83+
}
84+
if fetchPos {
85+
if err := encodeUint(enc, KeyFetchPos); err != nil {
86+
return err
87+
}
88+
if err := enc.EncodeBool(fetchPos); err != nil {
89+
return err
90+
}
91+
}
92+
if after != nil {
93+
if pos, ok := after.([]byte); ok {
94+
if err := encodeUint(enc, KeyAfterPos); err != nil {
95+
return err
96+
}
97+
if err := enc.EncodeString(string(pos)); err != nil {
98+
return err
99+
}
100+
} else {
101+
if err := encodeUint(enc, KeyAfterTuple); err != nil {
102+
return err
103+
}
104+
if err := enc.Encode(after); err != nil {
105+
return err
106+
}
107+
}
108+
}
109+
return nil
42110
}
43111

44112
func fillUpdate(enc *encoder, spaceNo, indexNo uint32, key, ops interface{}) error {
@@ -660,9 +728,9 @@ func (req *PingRequest) Context(ctx context.Context) *PingRequest {
660728
// by a Connection.
661729
type SelectRequest struct {
662730
spaceIndexRequest
663-
isIteratorSet bool
731+
isIteratorSet, fetchPos bool
664732
offset, limit, iterator uint32
665-
key interface{}
733+
key, after interface{}
666734
}
667735

668736
// NewSelectRequest returns a new empty SelectRequest.
@@ -671,8 +739,10 @@ func NewSelectRequest(space interface{}) *SelectRequest {
671739
req.requestCode = SelectRequestCode
672740
req.setSpace(space)
673741
req.isIteratorSet = false
742+
req.fetchPos = false
674743
req.iterator = IterAll
675744
req.key = []interface{}{}
745+
req.after = nil
676746
req.limit = 0xFFFFFFFF
677747
return req
678748
}
@@ -716,14 +786,39 @@ func (req *SelectRequest) Key(key interface{}) *SelectRequest {
716786
return req
717787
}
718788

789+
// FetchPos determines whether to fetch positions of the last tuple. A position
790+
// descriptor will be saved in Response.Pos value.
791+
//
792+
// Note: default value is false.
793+
//
794+
// Requires Tarantool >= 2.11.
795+
// Since 1.11.0
796+
func (req *SelectRequest) FetchPos(fetch bool) *SelectRequest {
797+
req.fetchPos = fetch
798+
return req
799+
}
800+
801+
// After must contain a tuple from which selection must continue or its
802+
// position (a value from Response.Pos).
803+
//
804+
// Note: default value in nil.
805+
//
806+
// Requires Tarantool >= 2.11.
807+
// Since 1.11.0
808+
func (req *SelectRequest) After(after interface{}) *SelectRequest {
809+
req.after = after
810+
return req
811+
}
812+
719813
// Body fills an encoder with the select request body.
720814
func (req *SelectRequest) Body(res SchemaResolver, enc *encoder) error {
721815
spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index)
722816
if err != nil {
723817
return err
724818
}
725819

726-
return fillSelect(enc, spaceNo, indexNo, req.offset, req.limit, req.iterator, req.key)
820+
return fillSelect(enc, spaceNo, indexNo, req.offset, req.limit, req.iterator,
821+
req.key, req.after, req.fetchPos)
727822
}
728823

729824
// Context sets a passed context to the request.

request_test.go

+36-10
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ func TestSelectRequestDefaultValues(t *testing.T) {
319319
var refBuf bytes.Buffer
320320

321321
refEnc := NewEncoder(&refBuf)
322-
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterAll, []interface{}{})
322+
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF,
323+
IterAll, []interface{}{}, nil, false)
323324
if err != nil {
324325
t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error())
325326
return
@@ -334,7 +335,8 @@ func TestSelectRequestDefaultIteratorEqIfKey(t *testing.T) {
334335
key := []interface{}{uint(18)}
335336

336337
refEnc := NewEncoder(&refBuf)
337-
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterEq, key)
338+
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF,
339+
IterEq, key, nil, false)
338340
if err != nil {
339341
t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error())
340342
return
@@ -351,7 +353,8 @@ func TestSelectRequestIteratorNotChangedIfKey(t *testing.T) {
351353
const iter = IterGe
352354

353355
refEnc := NewEncoder(&refBuf)
354-
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, iter, key)
356+
err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF,
357+
iter, key, nil, false)
355358
if err != nil {
356359
t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error())
357360
return
@@ -368,22 +371,45 @@ func TestSelectRequestSetters(t *testing.T) {
368371
const limit = 5
369372
const iter = IterLt
370373
key := []interface{}{uint(36)}
371-
var refBuf bytes.Buffer
374+
afterBytes := []byte{0x1, 0x2, 0x3}
375+
afterKey := []interface{}{uint(13)}
376+
var refBufAfterBytes, refBufAfterKey bytes.Buffer
372377

373-
refEnc := NewEncoder(&refBuf)
374-
err := RefImplSelectBody(refEnc, validSpace, validIndex, offset, limit, iter, key)
378+
refEncAfterBytes := NewEncoder(&refBufAfterBytes)
379+
err := RefImplSelectBody(refEncAfterBytes, validSpace, validIndex, offset,
380+
limit, iter, key, afterBytes, true)
375381
if err != nil {
376-
t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error())
382+
t.Errorf("An unexpected RefImplSelectBody() error %s", err)
377383
return
378384
}
379385

380-
req := NewSelectRequest(validSpace).
386+
refEncAfterKey := NewEncoder(&refBufAfterKey)
387+
err = RefImplSelectBody(refEncAfterKey, validSpace, validIndex, offset,
388+
limit, iter, key, afterKey, true)
389+
if err != nil {
390+
t.Errorf("An unexpected RefImplSelectBody() error %s", err)
391+
return
392+
}
393+
394+
reqAfterBytes := NewSelectRequest(validSpace).
381395
Index(validIndex).
382396
Offset(offset).
383397
Limit(limit).
384398
Iterator(iter).
385-
Key(key)
386-
assertBodyEqual(t, refBuf.Bytes(), req)
399+
Key(key).
400+
After(afterBytes).
401+
FetchPos(true)
402+
reqAfterKey := NewSelectRequest(validSpace).
403+
Index(validIndex).
404+
Offset(offset).
405+
Limit(limit).
406+
Iterator(iter).
407+
Key(key).
408+
After(afterKey).
409+
FetchPos(true)
410+
411+
assertBodyEqual(t, refBufAfterBytes.Bytes(), reqAfterBytes)
412+
assertBodyEqual(t, refBufAfterKey.Bytes(), reqAfterKey)
387413
}
388414

389415
func TestInsertRequestDefaultValues(t *testing.T) {

response.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import (
77
type Response struct {
88
RequestId uint32
99
Code uint32
10-
Error string // error message
11-
// Data contains deserialized data for untyped requests
12-
Data []interface{}
10+
// Error contains an error message.
11+
Error string
12+
// Data contains deserialized data for untyped requests.
13+
Data []interface{}
14+
// Pos contains a position descriptor of last selected tuple.
15+
Pos []byte
1316
MetaData []ColumnMetaData
1417
SQLInfo SQLInfo
1518
buf smallBuf
@@ -228,6 +231,10 @@ func (resp *Response) decodeBody() (err error) {
228231
if !found {
229232
return fmt.Errorf("unknown auth type %s", auth)
230233
}
234+
case KeyPos:
235+
if resp.Pos, err = d.DecodeBytes(); err != nil {
236+
return fmt.Errorf("unable to decode a position: %w", err)
237+
}
231238
default:
232239
if err = d.Skip(); err != nil {
233240
return err
@@ -300,6 +307,10 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) {
300307
if err = d.Decode(&resp.MetaData); err != nil {
301308
return err
302309
}
310+
case KeyPos:
311+
if resp.Pos, err = d.DecodeBytes(); err != nil {
312+
return fmt.Errorf("unable to decode a position: %w", err)
313+
}
303314
default:
304315
if err = d.Skip(); err != nil {
305316
return err

0 commit comments

Comments
 (0)