Skip to content

Commit

Permalink
api: support IPROTO_FEATURE_SPACE_AND_INDEX_NAMES
Browse files Browse the repository at this point in the history
Support `IPROTO_FEATURE_SPACE_AND_INDEX_NAMES` for Tarantool
version >= 3.0.0-alpha. It allows to use space and index names in requests
instead of their IDs.

`ResolveSpaceIndex` function for `SchemaResolver` interface split into two:
`ResolveSpace` and `ResolveIndex`. `NamesUseSupported` function added into the
interface to get information if usage of space and index names is supported.

`Schema` structure no longer implements `SchemaResolver` interface.

Part of #338
  • Loading branch information
DerekBum committed Nov 9, 2023
1 parent 772b21f commit 1f4c0ad
Show file tree
Hide file tree
Showing 12 changed files with 663 additions and 239 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
- Support `IPROTO_WATCH_ONCE` request type for Tarantool
version >= 3.0.0-alpha1 (#337)
- Support `yield_every` option for crud select requests (#350)
- Support `IPROTO_FEATURE_SPACE_AND_INDEX_NAMES` for Tarantool
version >= 3.0.0-alpha1 (#338). It allows to use space and index names
in requests instead of their IDs.

### Changed

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,14 @@ and user may cancel it in process.
* `iproto.Feature` type used instead of `ProtocolFeature`.
* `iproto.IPROTO_FEATURE_` constants used instead of local ones.

#### Schema changes

* `ResolveSpaceIndex` function for `SchemaResolver` interface split into two:
`ResolveSpace` and `ResolveIndex`. `NamesUseSupported` function added into the
interface to get information if the usage of space and index names in requests
is supported.
* `Schema` structure no longer implements `SchemaResolver` interface.

## Contributing

See [the contributing guide](CONTRIBUTING.md) for detailed instructions on how
Expand Down
10 changes: 9 additions & 1 deletion connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ type Connection struct {
cond *sync.Cond
// Schema contains schema loaded on connection.
Schema *Schema
// schemaResolver contains a SchemaResolver implementation.
schemaResolver SchemaResolver
// requestId contains the last request ID for requests with nil context.
requestId uint32
// contextRequestId contains the last request ID for requests with context.
Expand Down Expand Up @@ -426,6 +428,12 @@ func Connect(ctx context.Context, addr string, opts Opts) (conn *Connection, err
if err = conn.createConnection(ctx); err != nil {
return nil, err
}
conn.lockShards()
conn.schemaResolver = &noSchemaResolver{
isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
conn.serverProtocolInfo.Features),
}
conn.unlockShards()

go conn.pinger()
if conn.opts.Timeout > 0 {
Expand Down Expand Up @@ -1102,7 +1110,7 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) {
}
blen := shard.buf.Len()
reqid := fut.requestId
if err := pack(&shard.buf, shard.enc, reqid, req, streamId, conn.Schema); err != nil {
if err := pack(&shard.buf, shard.enc, reqid, req, streamId, conn.schemaResolver); err != nil {
shard.buf.Trunc(blen)
shard.bufmut.Unlock()
if f := conn.fetchFuture(reqid); f == fut {
Expand Down
43 changes: 4 additions & 39 deletions crud/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package crud_test
import (
"bytes"
"context"
"errors"
"fmt"
"testing"

Expand All @@ -14,14 +13,7 @@ import (
"github.com/tarantool/go-tarantool/v2/crud"
)

const invalidSpaceMsg = "invalid space"
const invalidIndexMsg = "invalid index"

const invalidSpace = 2
const invalidIndex = 2
const validSpace = "test" // Any valid value != default.
const defaultSpace = 0 // And valid too.
const defaultIndex = 0 // And valid too.

const CrudRequestType = iproto.IPROTO_CALL

Expand Down Expand Up @@ -69,38 +61,11 @@ var expectedOpts = map[string]interface{}{
"timeout": timeout,
}

type ValidSchemeResolver struct {
}

func (*ValidSchemeResolver) ResolveSpaceIndex(s, i interface{}) (uint32, uint32, error) {
var spaceNo, indexNo uint32
if s != nil {
spaceNo = uint32(s.(int))
} else {
spaceNo = defaultSpace
}
if i != nil {
indexNo = uint32(i.(int))
} else {
indexNo = defaultIndex
}
if spaceNo == invalidSpace {
return 0, 0, errors.New(invalidSpaceMsg)
}
if indexNo == invalidIndex {
return 0, 0, errors.New(invalidIndexMsg)
}
return spaceNo, indexNo, nil
}

var resolver ValidSchemeResolver

func extractRequestBody(req tarantool.Request,
resolver tarantool.SchemaResolver) ([]byte, error) {
func extractRequestBody(req tarantool.Request) ([]byte, error) {
var reqBuf bytes.Buffer
reqEnc := msgpack.NewEncoder(&reqBuf)

err := req.Body(resolver, reqEnc)
err := req.Body(nil, reqEnc)
if err != nil {
return nil, fmt.Errorf("An unexpected Response.Body() error: %q", err.Error())
}
Expand All @@ -111,12 +76,12 @@ func extractRequestBody(req tarantool.Request,
func assertBodyEqual(t testing.TB, reference tarantool.Request, req tarantool.Request) {
t.Helper()

reqBody, err := extractRequestBody(req, &resolver)
reqBody, err := extractRequestBody(req)
if err != nil {
t.Fatalf("An unexpected Response.Body() error: %q", err.Error())
}

refBody, err := extractRequestBody(reference, &resolver)
refBody, err := extractRequestBody(reference)
if err != nil {
t.Fatalf("An unexpected Response.Body() error: %q", err.Error())
}
Expand Down
88 changes: 88 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,21 @@ func ExampleSelectRequest() {
// response is [{{} 1111 hello world}]
}

func ExampleSelectRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewSelectRequest(spaceName)
req.Index(indexName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleInsertRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -273,6 +288,20 @@ func ExampleInsertRequest() {
// Data [[32 test one]]
}

func ExampleInsertRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewInsertRequest(spaceName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleDeleteRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -316,6 +345,21 @@ func ExampleDeleteRequest() {
// Data [[36 test one]]
}

func ExampleDeleteRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewDeleteRequest(spaceName)
req.Index(indexName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleReplaceRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -375,6 +419,20 @@ func ExampleReplaceRequest() {
// Data [[13 test twelve]]
}

func ExampleReplaceRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewReplaceRequest(spaceName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleUpdateRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -411,6 +469,21 @@ func ExampleUpdateRequest() {
// response is []interface {}{[]interface {}{0x457, "hello", "world"}}
}

func ExampleUpdateRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewUpdateRequest(spaceName)
req.Index(indexName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleUpsertRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -452,6 +525,20 @@ func ExampleUpsertRequest() {
// response is []interface {}{[]interface {}{0x459, "first", "updated"}}
}

func ExampleUpsertRequest_spaceAndIndexNames() {
conn := exampleConnect(opts)
defer conn.Close()

req := tarantool.NewUpsertRequest(spaceName)
resp, err := conn.Do(req).Get()

if err != nil {
fmt.Printf("Failed to execute the request: %s\n", err)
} else {
fmt.Println(resp.Data)
}
}

func ExampleCallRequest() {
conn := exampleConnect(opts)
defer conn.Close()
Expand Down Expand Up @@ -634,6 +721,7 @@ func ExampleProtocolVersion() {
// Connector client protocol feature: IPROTO_FEATURE_ERROR_EXTENSION
// Connector client protocol feature: IPROTO_FEATURE_WATCHERS
// Connector client protocol feature: IPROTO_FEATURE_PAGINATION
// Connector client protocol feature: IPROTO_FEATURE_SPACE_AND_INDEX_NAMES
// Connector client protocol feature: IPROTO_FEATURE_WATCH_ONCE
}

Expand Down
67 changes: 54 additions & 13 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,80 @@ func RefImplPingBody(enc *msgpack.Encoder) error {

// RefImplSelectBody is reference implementation for filling of a select
// request's body.
func RefImplSelectBody(enc *msgpack.Encoder, space, index, offset, limit uint32, iterator Iter,
key, after interface{}, fetchPos bool) error {
return fillSelect(enc, space, index, offset, limit, iterator, key, after, fetchPos)
func RefImplSelectBody(enc *msgpack.Encoder, res SchemaResolver, space, index interface{},
offset, limit uint32, iterator Iter, key, after interface{}, fetchPos bool) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
indexEnc, err := newIndexEncoder(res, index, spaceEnc.Id)
if err != nil {
return err
}
return fillSelect(enc, spaceEnc, indexEnc, offset, limit, iterator, key, after, fetchPos)
}

// RefImplInsertBody is reference implementation for filling of an insert
// request's body.
func RefImplInsertBody(enc *msgpack.Encoder, space uint32, tuple interface{}) error {
return fillInsert(enc, space, tuple)
func RefImplInsertBody(enc *msgpack.Encoder, res SchemaResolver, space,
tuple interface{}) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
return fillInsert(enc, spaceEnc, tuple)
}

// RefImplReplaceBody is reference implementation for filling of a replace
// request's body.
func RefImplReplaceBody(enc *msgpack.Encoder, space uint32, tuple interface{}) error {
return fillInsert(enc, space, tuple)
func RefImplReplaceBody(enc *msgpack.Encoder, res SchemaResolver, space,
tuple interface{}) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
return fillInsert(enc, spaceEnc, tuple)
}

// RefImplDeleteBody is reference implementation for filling of a delete
// request's body.
func RefImplDeleteBody(enc *msgpack.Encoder, space, index uint32, key interface{}) error {
return fillDelete(enc, space, index, key)
func RefImplDeleteBody(enc *msgpack.Encoder, res SchemaResolver, space, index,
key interface{}) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
indexEnc, err := newIndexEncoder(res, index, spaceEnc.Id)
if err != nil {
return err
}
return fillDelete(enc, spaceEnc, indexEnc, key)
}

// RefImplUpdateBody is reference implementation for filling of an update
// request's body.
func RefImplUpdateBody(enc *msgpack.Encoder, space, index uint32, key, ops interface{}) error {
return fillUpdate(enc, space, index, key, ops)
func RefImplUpdateBody(enc *msgpack.Encoder, res SchemaResolver, space, index,
key, ops interface{}) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
indexEnc, err := newIndexEncoder(res, index, spaceEnc.Id)
if err != nil {
return err
}
return fillUpdate(enc, spaceEnc, indexEnc, key, ops)
}

// RefImplUpsertBody is reference implementation for filling of an upsert
// request's body.
func RefImplUpsertBody(enc *msgpack.Encoder, space uint32, tuple, ops interface{}) error {
return fillUpsert(enc, space, tuple, ops)
func RefImplUpsertBody(enc *msgpack.Encoder, res SchemaResolver, space,
tuple, ops interface{}) error {
spaceEnc, err := newSpaceEncoder(res, space)
if err != nil {
return err
}
return fillUpsert(enc, spaceEnc, tuple, ops)
}

// RefImplCallBody is reference implementation for filling of a call or call17
Expand Down
1 change: 1 addition & 0 deletions protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var clientProtocolInfo ProtocolInfo = ProtocolInfo{
iproto.IPROTO_FEATURE_ERROR_EXTENSION,
iproto.IPROTO_FEATURE_WATCHERS,
iproto.IPROTO_FEATURE_PAGINATION,
iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
iproto.IPROTO_FEATURE_WATCH_ONCE,
},
}
Expand Down
Loading

0 comments on commit 1f4c0ad

Please sign in to comment.