Skip to content

api: add pagination support #261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,43 @@ jobs:
- name: Clone the connector
uses: actions/checkout@v2

- name: Setup tt
run: |
curl -L https://tarantool.io/release/2/installer.sh | sudo bash
sudo apt install -y tt

- name: Setup Tarantool ${{ matrix.tarantool }}
if: matrix.tarantool != '2.x-latest'
uses: tarantool/setup-tarantool@v2
with:
tarantool-version: ${{ matrix.tarantool }}

- name: Setup Tarantool 2.x (latest)
- name: Get Tarantool 2.x latest commit
if: matrix.tarantool == '2.x-latest'
run: |
curl -L https://tarantool.io/pre-release/2/installer.sh | sudo bash
sudo apt install -y tarantool tarantool-dev
commit_hash=$(git ls-remote https://github.com/tarantool/tarantool.git --branch master | head -c 8)
echo "LATEST_COMMIT=${commit_hash}" >> $GITHUB_ENV
shell: bash

- name: Cache Tarantool 2.x latest
if: matrix.tarantool == '2.x-latest'
id: cache-latest
uses: actions/cache@v3
with:
path: "/opt/tarantool"
key: cache-latest-${{ env.LATEST_COMMIT }}

- name: Setup Tarantool 2.x latest
if: matrix.tarantool == '2.x-latest' && steps.cache-latest.outputs.cache-hit != 'true'
run: |
# mkdir could be removed after:
# https://github.com/tarantool/tt/issues/282
sudo mkdir -p /opt/tarantool
sudo tt install tarantool=master

- name: Add Tarantool 2.x latest to PATH
if: matrix.tarantool == '2.x-latest'
run: echo "/opt/tarantool/bin" >> $GITHUB_PATH

- name: Setup golang for the connector and tests
uses: actions/setup-go@v2
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.

### Added

- Support pagination (#246)

### Changed

### Fixed
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ BENCH_OPTIONS := -bench=. -run=^Benchmark -benchmem -benchtime=${DURATION} -coun
GO_TARANTOOL_URL := https://github.com/tarantool/go-tarantool
GO_TARANTOOL_DIR := ${PROJECT_DIR}/${BENCH_PATH}/go-tarantool
TAGS :=
TTCTL := tt
ifeq (,$(shell which tt 2>/dev/null))
TTCTL := tarantoolctl
endif

.PHONY: clean
clean:
Expand All @@ -22,7 +26,7 @@ clean:

.PHONY: deps
deps: clean
( cd ./queue/testdata; tarantoolctl rocks install queue 1.2.1 )
( cd ./queue/testdata; $(TTCTL) rocks install queue 1.2.1 )

.PHONY: datetime-timezones
datetime-timezones:
Expand Down
4 changes: 4 additions & 0 deletions const.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,20 @@ const (
KeyLimit = 0x12
KeyOffset = 0x13
KeyIterator = 0x14
KeyFetchPos = 0x1f
KeyKey = 0x20
KeyTuple = 0x21
KeyFunctionName = 0x22
KeyUserName = 0x23
KeyExpression = 0x27
KeyAfterPos = 0x2e
KeyAfterTuple = 0x2f
KeyDefTuple = 0x28
KeyData = 0x30
KeyError24 = 0x31 /* Error in pre-2.4 format. */
KeyMetaData = 0x32
KeyBindCount = 0x34
KeyPos = 0x35
KeySQLText = 0x40
KeySQLBind = 0x41
KeySQLInfo = 0x42
Expand Down
2 changes: 1 addition & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func ExampleProtocolVersion() {
fmt.Println("Connector client protocol features:", clientProtocolInfo.Features)
// Output:
// Connector client protocol version: 4
// Connector client protocol features: [StreamsFeature TransactionsFeature ErrorExtensionFeature WatchersFeature]
// Connector client protocol features: [StreamsFeature TransactionsFeature ErrorExtensionFeature WatchersFeature PaginationFeature]
}

func getTestTxnOpts() tarantool.Opts {
Expand Down
5 changes: 3 additions & 2 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ func RefImplPingBody(enc *encoder) error {

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

// RefImplInsertBody is reference implementation for filling of an insert
Expand Down
7 changes: 5 additions & 2 deletions protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ const (
// (supported by connector).
TransactionsFeature ProtocolFeature = 1
// ErrorExtensionFeature represents support of MP_ERROR objects over MessagePack
// (unsupported by connector).
// (supported by connector).
ErrorExtensionFeature ProtocolFeature = 2
// WatchersFeature represents support of watchers
// (supported by connector).
WatchersFeature ProtocolFeature = 3
// PaginationFeature represents support of pagination
// (unsupported by connector).
// (supported by connector).
PaginationFeature ProtocolFeature = 4
)

Expand Down Expand Up @@ -83,11 +83,14 @@ var clientProtocolInfo ProtocolInfo = ProtocolInfo{
// version 2 (Tarantool 2.10.0), in connector since 1.10.0.
// Watchers were introduced in protocol version 3 (Tarantool 2.10.0), in
// connector since 1.10.0.
// Pagination were introduced in protocol version 4 (Tarantool 2.11.0), in
// connector since 1.11.0.
Features: []ProtocolFeature{
StreamsFeature,
TransactionsFeature,
ErrorExtensionFeature,
WatchersFeature,
PaginationFeature,
},
}

Expand Down
141 changes: 118 additions & 23 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,103 @@ import (
)

func fillSearch(enc *encoder, spaceNo, indexNo uint32, key interface{}) error {
encodeUint(enc, KeySpaceNo)
encodeUint(enc, uint64(spaceNo))
encodeUint(enc, KeyIndexNo)
encodeUint(enc, uint64(indexNo))
encodeUint(enc, KeyKey)
if err := encodeUint(enc, KeySpaceNo); err != nil {
return err
}
if err := encodeUint(enc, uint64(spaceNo)); err != nil {
return err
}
if err := encodeUint(enc, KeyIndexNo); err != nil {
return err
}
if err := encodeUint(enc, uint64(indexNo)); err != nil {
return err
}
if err := encodeUint(enc, KeyKey); err != nil {
return err
}
return enc.Encode(key)
}

func fillIterator(enc *encoder, offset, limit, iterator uint32) {
encodeUint(enc, KeyIterator)
encodeUint(enc, uint64(iterator))
encodeUint(enc, KeyOffset)
encodeUint(enc, uint64(offset))
encodeUint(enc, KeyLimit)
encodeUint(enc, uint64(limit))
func fillIterator(enc *encoder, offset, limit, iterator uint32) error {
if err := encodeUint(enc, KeyIterator); err != nil {
return err
}
if err := encodeUint(enc, uint64(iterator)); err != nil {
return err
}
if err := encodeUint(enc, KeyOffset); err != nil {
return err
}
if err := encodeUint(enc, uint64(offset)); err != nil {
return err
}
if err := encodeUint(enc, KeyLimit); err != nil {
return err
}
return encodeUint(enc, uint64(limit))
}

func fillInsert(enc *encoder, spaceNo uint32, tuple interface{}) error {
enc.EncodeMapLen(2)
encodeUint(enc, KeySpaceNo)
encodeUint(enc, uint64(spaceNo))
encodeUint(enc, KeyTuple)
if err := enc.EncodeMapLen(2); err != nil {
return err
}
if err := encodeUint(enc, KeySpaceNo); err != nil {
return err
}
if err := encodeUint(enc, uint64(spaceNo)); err != nil {
return err
}
if err := encodeUint(enc, KeyTuple); err != nil {
return err
}
return enc.Encode(tuple)
}

func fillSelect(enc *encoder, spaceNo, indexNo, offset, limit, iterator uint32, key interface{}) error {
enc.EncodeMapLen(6)
fillIterator(enc, offset, limit, iterator)
return fillSearch(enc, spaceNo, indexNo, key)
func fillSelect(enc *encoder, spaceNo, indexNo, offset, limit, iterator uint32,
key, after interface{}, fetchPos bool) error {
mapLen := 6
if fetchPos {
mapLen += 1
}
if after != nil {
mapLen += 1
}
if err := enc.EncodeMapLen(mapLen); err != nil {
return err
}
if err := fillIterator(enc, offset, limit, iterator); err != nil {
return err
}
if err := fillSearch(enc, spaceNo, indexNo, key); err != nil {
return err
}
if fetchPos {
if err := encodeUint(enc, KeyFetchPos); err != nil {
return err
}
if err := enc.EncodeBool(fetchPos); err != nil {
return err
}
}
if after != nil {
if pos, ok := after.([]byte); ok {
if err := encodeUint(enc, KeyAfterPos); err != nil {
return err
}
if err := enc.EncodeString(string(pos)); err != nil {
return err
}
} else {
if err := encodeUint(enc, KeyAfterTuple); err != nil {
return err
}
if err := enc.Encode(after); err != nil {
return err
}
}
}
return nil
}

func fillUpdate(enc *encoder, spaceNo, indexNo uint32, key, ops interface{}) error {
Expand Down Expand Up @@ -660,9 +728,9 @@ func (req *PingRequest) Context(ctx context.Context) *PingRequest {
// by a Connection.
type SelectRequest struct {
spaceIndexRequest
isIteratorSet bool
isIteratorSet, fetchPos bool
offset, limit, iterator uint32
key interface{}
key, after interface{}
}

// NewSelectRequest returns a new empty SelectRequest.
Expand All @@ -671,8 +739,10 @@ func NewSelectRequest(space interface{}) *SelectRequest {
req.requestCode = SelectRequestCode
req.setSpace(space)
req.isIteratorSet = false
req.fetchPos = false
req.iterator = IterAll
req.key = []interface{}{}
req.after = nil
req.limit = 0xFFFFFFFF
return req
}
Expand Down Expand Up @@ -716,14 +786,39 @@ func (req *SelectRequest) Key(key interface{}) *SelectRequest {
return req
}

// FetchPos determines whether to fetch positions of the last tuple. A position
// descriptor will be saved in Response.Pos value.
//
// Note: default value is false.
//
// Requires Tarantool >= 2.11.
// Since 1.11.0
func (req *SelectRequest) FetchPos(fetch bool) *SelectRequest {
req.fetchPos = fetch
return req
}

// After must contain a tuple from which selection must continue or its
// position (a value from Response.Pos).
//
// Note: default value in nil.
//
// Requires Tarantool >= 2.11.
// Since 1.11.0
func (req *SelectRequest) After(after interface{}) *SelectRequest {
req.after = after
return req
}

// Body fills an encoder with the select request body.
func (req *SelectRequest) Body(res SchemaResolver, enc *encoder) error {
spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index)
if err != nil {
return err
}

return fillSelect(enc, spaceNo, indexNo, req.offset, req.limit, req.iterator, req.key)
return fillSelect(enc, spaceNo, indexNo, req.offset, req.limit, req.iterator,
req.key, req.after, req.fetchPos)
}

// Context sets a passed context to the request.
Expand Down
Loading