From 9b8d04093de1a2512b788e580e3c09602a545f20 Mon Sep 17 00:00:00 2001 From: DerekBum Date: Wed, 1 Nov 2023 20:26:03 +0300 Subject: [PATCH 1/3] api: support `IPROTO_FEATURE_SPACE_AND_INDEX_NAMES` Support `IPROTO_FEATURE_SPACE_AND_INDEX_NAMES` for Tarantool version >= 3.0.0-alpha1. 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 --- CHANGELOG.md | 3 + README.md | 8 ++ connection.go | 12 +- crud/request_test.go | 43 +----- example_test.go | 88 +++++++++++++ export_test.go | 67 ++++++++-- protocol.go | 1 + request.go | 149 ++++++++++++++++----- request_test.go | 276 ++++++++++++++++++++++++++++++++++----- schema.go | 189 ++++++++++++++++++--------- settings/request_test.go | 26 ++-- tarantool_test.go | 45 +------ 12 files changed, 668 insertions(+), 239 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cdf0012..8500568ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index eae6f92ab..f8483dc34 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/connection.go b/connection.go index 018df6caa..217c153dd 100644 --- a/connection.go +++ b/connection.go @@ -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. @@ -528,6 +530,14 @@ func (conn *Connection) dial(ctx context.Context) error { conn.Greeting.Version = c.Greeting().Version conn.serverProtocolInfo = c.ProtocolInfo() + spaceAndIndexNamesSupported := + isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, + conn.serverProtocolInfo.Features) + + conn.schemaResolver = &noSchemaResolver{ + SpaceAndIndexNamesSupported: spaceAndIndexNamesSupported, + } + // Watchers. conn.watchMap.Range(func(key, value interface{}) bool { st := value.(chan watchState) @@ -1102,7 +1112,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 { diff --git a/crud/request_test.go b/crud/request_test.go index 8a64ab427..0b728162e 100644 --- a/crud/request_test.go +++ b/crud/request_test.go @@ -3,7 +3,6 @@ package crud_test import ( "bytes" "context" - "errors" "fmt" "testing" @@ -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 @@ -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()) } @@ -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()) } diff --git a/example_test.go b/example_test.go index f85f532d0..9b66ea29b 100644 --- a/example_test.go +++ b/example_test.go @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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 } diff --git a/export_test.go b/export_test.go index 38211a4fc..a52ef5b26 100644 --- a/export_test.go +++ b/export_test.go @@ -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 diff --git a/protocol.go b/protocol.go index 7de8b197e..9296943ce 100644 --- a/protocol.go +++ b/protocol.go @@ -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, }, } diff --git a/request.go b/request.go index 3332486f4..c2e7cf96d 100644 --- a/request.go +++ b/request.go @@ -12,19 +12,90 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -func fillSearch(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) error { - if err := enc.EncodeUint(uint64(iproto.IPROTO_SPACE_ID)); err != nil { +type spaceEncoder struct { + Id uint32 + Name string + IsId bool +} + +func newSpaceEncoder(res SchemaResolver, spaceInfo interface{}) (spaceEncoder, error) { + if res.NamesUseSupported() { + if spaceName, ok := spaceInfo.(string); ok { + return spaceEncoder{ + Id: 0, + Name: spaceName, + IsId: false, + }, nil + } + } + + spaceId, err := res.ResolveSpace(spaceInfo) + return spaceEncoder{ + Id: spaceId, + IsId: true, + }, err +} + +func (e spaceEncoder) Encode(enc *msgpack.Encoder) error { + if e.IsId { + if err := enc.EncodeUint(uint64(iproto.IPROTO_SPACE_ID)); err != nil { + return err + } + return enc.EncodeUint(uint64(e.Id)) + } + if err := enc.EncodeUint(uint64(iproto.IPROTO_SPACE_NAME)); err != nil { return err } - if err := enc.EncodeUint(uint64(spaceNo)); err != nil { + return enc.EncodeString(e.Name) +} + +type indexEncoder struct { + Id uint32 + Name string + IsId bool +} + +func newIndexEncoder(res SchemaResolver, indexInfo interface{}, + spaceNo uint32) (indexEncoder, error) { + if res.NamesUseSupported() { + if indexName, ok := indexInfo.(string); ok { + return indexEncoder{ + Name: indexName, + IsId: false, + }, nil + } + } + + indexId, err := res.ResolveIndex(indexInfo, spaceNo) + return indexEncoder{ + Id: indexId, + IsId: true, + }, err +} + +func (e indexEncoder) Encode(enc *msgpack.Encoder) error { + if e.IsId { + if err := enc.EncodeUint(uint64(iproto.IPROTO_INDEX_ID)); err != nil { + return err + } + return enc.EncodeUint(uint64(e.Id)) + } + if err := enc.EncodeUint(uint64(iproto.IPROTO_INDEX_NAME)); err != nil { return err } - if err := enc.EncodeUint(uint64(iproto.IPROTO_INDEX_ID)); err != nil { + return enc.EncodeString(e.Name) +} + +func fillSearch(enc *msgpack.Encoder, spaceEnc spaceEncoder, indexEnc indexEncoder, + key interface{}) error { + if err := spaceEnc.Encode(enc); err != nil { return err } - if err := enc.EncodeUint(uint64(indexNo)); err != nil { + + if err := indexEnc.Encode(enc); err != nil { return err } + if err := enc.EncodeUint(uint64(iproto.IPROTO_KEY)); err != nil { return err } @@ -50,24 +121,22 @@ func fillIterator(enc *msgpack.Encoder, offset, limit uint32, iterator Iter) err return enc.EncodeUint(uint64(limit)) } -func fillInsert(enc *msgpack.Encoder, spaceNo uint32, tuple interface{}) error { +func fillInsert(enc *msgpack.Encoder, spaceEnc spaceEncoder, tuple interface{}) error { if err := enc.EncodeMapLen(2); err != nil { return err } - if err := enc.EncodeUint(uint64(iproto.IPROTO_SPACE_ID)); err != nil { - return err - } - if err := enc.EncodeUint(uint64(spaceNo)); err != nil { + if err := spaceEnc.Encode(enc); err != nil { return err } + if err := enc.EncodeUint(uint64(iproto.IPROTO_TUPLE)); err != nil { return err } return enc.Encode(tuple) } -func fillSelect(enc *msgpack.Encoder, spaceNo, indexNo, offset, limit uint32, iterator Iter, - key, after interface{}, fetchPos bool) error { +func fillSelect(enc *msgpack.Encoder, spaceEnc spaceEncoder, indexEnc indexEncoder, + offset, limit uint32, iterator Iter, key, after interface{}, fetchPos bool) error { mapLen := 6 if fetchPos { mapLen += 1 @@ -81,7 +150,7 @@ func fillSelect(enc *msgpack.Encoder, spaceNo, indexNo, offset, limit uint32, it if err := fillIterator(enc, offset, limit, iterator); err != nil { return err } - if err := fillSearch(enc, spaceNo, indexNo, key); err != nil { + if err := fillSearch(enc, spaceEnc, indexEnc, key); err != nil { return err } if fetchPos { @@ -112,19 +181,22 @@ func fillSelect(enc *msgpack.Encoder, spaceNo, indexNo, offset, limit uint32, it return nil } -func fillUpdate(enc *msgpack.Encoder, spaceNo, indexNo uint32, key, ops interface{}) error { +func fillUpdate(enc *msgpack.Encoder, spaceEnc spaceEncoder, indexEnc indexEncoder, + key, ops interface{}) error { enc.EncodeMapLen(4) - if err := fillSearch(enc, spaceNo, indexNo, key); err != nil { + if err := fillSearch(enc, spaceEnc, indexEnc, key); err != nil { return err } enc.EncodeUint(uint64(iproto.IPROTO_TUPLE)) return enc.Encode(ops) } -func fillUpsert(enc *msgpack.Encoder, spaceNo uint32, tuple, ops interface{}) error { +func fillUpsert(enc *msgpack.Encoder, spaceEnc spaceEncoder, tuple, ops interface{}) error { enc.EncodeMapLen(3) - enc.EncodeUint(uint64(iproto.IPROTO_SPACE_ID)) - enc.EncodeUint(uint64(spaceNo)) + if err := spaceEnc.Encode(enc); err != nil { + return err + } + enc.EncodeUint(uint64(iproto.IPROTO_TUPLE)) if err := enc.Encode(tuple); err != nil { return err @@ -133,9 +205,10 @@ func fillUpsert(enc *msgpack.Encoder, spaceNo uint32, tuple, ops interface{}) er return enc.Encode(ops) } -func fillDelete(enc *msgpack.Encoder, spaceNo, indexNo uint32, key interface{}) error { +func fillDelete(enc *msgpack.Encoder, spaceEnc spaceEncoder, indexEnc indexEncoder, + key interface{}) error { enc.EncodeMapLen(3) - return fillSearch(enc, spaceNo, indexNo, key) + return fillSearch(enc, spaceEnc, indexEnc, key) } func fillCall(enc *msgpack.Encoder, functionName string, args interface{}) error { @@ -948,12 +1021,16 @@ func (req *SelectRequest) After(after interface{}) *SelectRequest { // Body fills an encoder with the select request body. func (req *SelectRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) + spaceEnc, err := newSpaceEncoder(res, req.space) + if err != nil { + return err + } + indexEnc, err := newIndexEncoder(res, req.index, spaceEnc.Id) if err != nil { return err } - return fillSelect(enc, spaceNo, indexNo, req.offset, req.limit, req.iterator, + return fillSelect(enc, spaceEnc, indexEnc, req.offset, req.limit, req.iterator, req.key, req.after, req.fetchPos) } @@ -993,12 +1070,12 @@ func (req *InsertRequest) Tuple(tuple interface{}) *InsertRequest { // Body fills an msgpack.Encoder with the insert request body. func (req *InsertRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) + spaceEnc, err := newSpaceEncoder(res, req.space) if err != nil { return err } - return fillInsert(enc, spaceNo, req.tuple) + return fillInsert(enc, spaceEnc, req.tuple) } // Context sets a passed context to the request. @@ -1037,12 +1114,12 @@ func (req *ReplaceRequest) Tuple(tuple interface{}) *ReplaceRequest { // Body fills an msgpack.Encoder with the replace request body. func (req *ReplaceRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) + spaceEnc, err := newSpaceEncoder(res, req.space) if err != nil { return err } - return fillInsert(enc, spaceNo, req.tuple) + return fillInsert(enc, spaceEnc, req.tuple) } // Context sets a passed context to the request. @@ -1088,12 +1165,16 @@ func (req *DeleteRequest) Key(key interface{}) *DeleteRequest { // Body fills an msgpack.Encoder with the delete request body. func (req *DeleteRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) + spaceEnc, err := newSpaceEncoder(res, req.space) + if err != nil { + return err + } + indexEnc, err := newIndexEncoder(res, req.index, spaceEnc.Id) if err != nil { return err } - return fillDelete(enc, spaceNo, indexNo, req.key) + return fillDelete(enc, spaceEnc, indexEnc, req.key) } // Context sets a passed context to the request. @@ -1150,12 +1231,16 @@ func (req *UpdateRequest) Operations(ops *Operations) *UpdateRequest { // Body fills an msgpack.Encoder with the update request body. func (req *UpdateRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, indexNo, err := res.ResolveSpaceIndex(req.space, req.index) + spaceEnc, err := newSpaceEncoder(res, req.space) + if err != nil { + return err + } + indexEnc, err := newIndexEncoder(res, req.index, spaceEnc.Id) if err != nil { return err } - return fillUpdate(enc, spaceNo, indexNo, req.key, req.ops) + return fillUpdate(enc, spaceEnc, indexEnc, req.key, req.ops) } // Context sets a passed context to the request. @@ -1205,12 +1290,12 @@ func (req *UpsertRequest) Operations(ops *Operations) *UpsertRequest { // Body fills an msgpack.Encoder with the upsert request body. func (req *UpsertRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { - spaceNo, _, err := res.ResolveSpaceIndex(req.space, nil) + spaceEnc, err := newSpaceEncoder(res, req.space) if err != nil { return err } - return fillUpsert(enc, spaceNo, req.tuple, req.ops) + return fillUpsert(enc, spaceEnc, req.tuple, req.ops) } // Context sets a passed context to the request. diff --git a/request_test.go b/request_test.go index 6d08533d3..f5968b738 100644 --- a/request_test.go +++ b/request_test.go @@ -17,14 +17,14 @@ import ( const invalidSpaceMsg = "invalid space" const invalidIndexMsg = "invalid index" -const invalidSpace = 2 -const invalidIndex = 2 -const validSpace = 1 // Any valid value != default. -const validIndex = 3 // Any valid value != default. +const invalidSpace uint32 = 2 +const invalidIndex uint32 = 2 +const validSpace uint32 = 1 // Any valid value != default. +const validIndex uint32 = 3 // Any valid value != default. const validExpr = "any string" // We don't check the value here. const validKey = "foo" // Any string. -const defaultSpace = 0 // And valid too. -const defaultIndex = 0 // And valid too. +const defaultSpace uint32 = 0 // And valid too. +const defaultIndex uint32 = 0 // And valid too. const defaultIsolationLevel = DefaultIsolationLevel const defaultTimeout = 0 @@ -39,27 +39,43 @@ var validProtocolInfo ProtocolInfo = ProtocolInfo{ } type ValidSchemeResolver struct { + nameUseSupported bool + spaceResolverCalls int + indexResolverCalls int } -func (*ValidSchemeResolver) ResolveSpaceIndex(s, i interface{}) (uint32, uint32, error) { - var spaceNo, indexNo uint32 - if s != nil { - spaceNo = uint32(s.(int)) +func (r *ValidSchemeResolver) ResolveSpace(s interface{}) (uint32, error) { + r.spaceResolverCalls++ + + var spaceNo uint32 + if no, ok := s.(uint32); ok { + spaceNo = no } else { spaceNo = defaultSpace } - if i != nil { - indexNo = uint32(i.(int)) + if spaceNo == invalidSpace { + return 0, errors.New(invalidSpaceMsg) + } + return spaceNo, nil +} + +func (r *ValidSchemeResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) { + r.indexResolverCalls++ + + var indexNo uint32 + if no, ok := i.(uint32); ok { + indexNo = no } else { indexNo = defaultIndex } - if spaceNo == invalidSpace { - return 0, 0, errors.New(invalidSpaceMsg) - } if indexNo == invalidIndex { - return 0, 0, errors.New(invalidIndexMsg) + return 0, errors.New(invalidIndexMsg) } - return spaceNo, indexNo, nil + return indexNo, nil +} + +func (r *ValidSchemeResolver) NamesUseSupported() bool { + return r.nameUseSupported } var resolver ValidSchemeResolver @@ -313,6 +329,58 @@ func TestRequestsCtx_setter(t *testing.T) { } } +func TestResolverCalledWithoutNameSupport(t *testing.T) { + resolver.nameUseSupported = false + resolver.spaceResolverCalls = 0 + resolver.indexResolverCalls = 0 + + req := NewSelectRequest("valid") + req.Index("valid") + + var reqBuf bytes.Buffer + reqEnc := msgpack.NewEncoder(&reqBuf) + + err := req.Body(&resolver, reqEnc) + if err != nil { + t.Errorf("An unexpected Response.Body() error: %q", err.Error()) + } + + if resolver.spaceResolverCalls != 1 { + t.Errorf("ResolveSpace was called %d times instead of 1.", + resolver.spaceResolverCalls) + } + if resolver.indexResolverCalls != 1 { + t.Errorf("ResolveIndex was called %d times instead of 1.", + resolver.indexResolverCalls) + } +} + +func TestResolverNotCalledWithNameSupport(t *testing.T) { + resolver.nameUseSupported = true + resolver.spaceResolverCalls = 0 + resolver.indexResolverCalls = 0 + + req := NewSelectRequest("valid") + req.Index("valid") + + var reqBuf bytes.Buffer + reqEnc := msgpack.NewEncoder(&reqBuf) + + err := req.Body(&resolver, reqEnc) + if err != nil { + t.Errorf("An unexpected Response.Body() error: %q", err.Error()) + } + + if resolver.spaceResolverCalls != 0 { + t.Errorf("ResolveSpace was called %d times instead of 0.", + resolver.spaceResolverCalls) + } + if resolver.indexResolverCalls != 0 { + t.Errorf("ResolveIndex was called %d times instead of 0.", + resolver.indexResolverCalls) + } +} + func TestPingRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer @@ -331,7 +399,7 @@ func TestSelectRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, + err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterAll, []interface{}{}, nil, false) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -342,12 +410,45 @@ func TestSelectRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestSelectRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplSelectBody(refEnc, &resolver, "valid", defaultIndex, 0, 0xFFFFFFFF, + IterAll, []interface{}{}, nil, false) + if err != nil { + t.Fatalf("An unexpected RefImplSelectBody() error %q", err.Error()) + } + + req := NewSelectRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestSelectRequestIndexByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplSelectBody(refEnc, &resolver, defaultSpace, "valid", 0, 0xFFFFFFFF, + IterAll, []interface{}{}, nil, false) + if err != nil { + t.Fatalf("An unexpected RefImplSelectBody() error %q", err.Error()) + } + + req := NewSelectRequest(defaultSpace) + req.Index("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestSelectRequestDefaultIteratorEqIfKey(t *testing.T) { var refBuf bytes.Buffer key := []interface{}{uint(18)} refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, + err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterEq, key, nil, false) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -365,7 +466,7 @@ func TestSelectRequestIteratorNotChangedIfKey(t *testing.T) { const iter = IterGe refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplSelectBody(refEnc, validSpace, defaultIndex, 0, 0xFFFFFFFF, + err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, iter, key, nil, false) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) @@ -388,7 +489,7 @@ func TestSelectRequestSetters(t *testing.T) { var refBufAfterBytes, refBufAfterKey bytes.Buffer refEncAfterBytes := msgpack.NewEncoder(&refBufAfterBytes) - err := RefImplSelectBody(refEncAfterBytes, validSpace, validIndex, offset, + err := RefImplSelectBody(refEncAfterBytes, &resolver, validSpace, validIndex, offset, limit, iter, key, afterBytes, true) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %s", err) @@ -396,7 +497,7 @@ func TestSelectRequestSetters(t *testing.T) { } refEncAfterKey := msgpack.NewEncoder(&refBufAfterKey) - err = RefImplSelectBody(refEncAfterKey, validSpace, validIndex, offset, + err = RefImplSelectBody(refEncAfterKey, &resolver, validSpace, validIndex, offset, limit, iter, key, afterKey, true) if err != nil { t.Errorf("An unexpected RefImplSelectBody() error %s", err) @@ -428,7 +529,7 @@ func TestInsertRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplInsertBody(refEnc, validSpace, []interface{}{}) + err := RefImplInsertBody(refEnc, &resolver, validSpace, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) return @@ -438,12 +539,27 @@ func TestInsertRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestInsertRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplInsertBody(refEnc, &resolver, "valid", []interface{}{}) + if err != nil { + t.Fatalf("An unexpected RefImplInsertBody() error: %q", err.Error()) + } + + req := NewInsertRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestInsertRequestSetters(t *testing.T) { tuple := []interface{}{uint(24)} var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplInsertBody(refEnc, validSpace, tuple) + err := RefImplInsertBody(refEnc, &resolver, validSpace, tuple) if err != nil { t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) return @@ -458,7 +574,7 @@ func TestReplaceRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplReplaceBody(refEnc, validSpace, []interface{}{}) + err := RefImplReplaceBody(refEnc, &resolver, validSpace, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) return @@ -468,12 +584,28 @@ func TestReplaceRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestReplaceRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplReplaceBody(refEnc, &resolver, "valid", []interface{}{}) + if err != nil { + t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) + return + } + + req := NewReplaceRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestReplaceRequestSetters(t *testing.T) { tuple := []interface{}{uint(99)} var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplReplaceBody(refEnc, validSpace, tuple) + err := RefImplReplaceBody(refEnc, &resolver, validSpace, tuple) if err != nil { t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) return @@ -488,7 +620,7 @@ func TestDeleteRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplDeleteBody(refEnc, validSpace, defaultIndex, []interface{}{}) + err := RefImplDeleteBody(refEnc, &resolver, validSpace, defaultIndex, []interface{}{}) if err != nil { t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) return @@ -498,12 +630,43 @@ func TestDeleteRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestDeleteRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplDeleteBody(refEnc, &resolver, "valid", defaultIndex, []interface{}{}) + if err != nil { + t.Fatalf("An unexpected RefImplDeleteBody() error: %q", err.Error()) + } + + req := NewDeleteRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestDeleteRequestIndexByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplDeleteBody(refEnc, &resolver, defaultSpace, "valid", []interface{}{}) + if err != nil { + t.Fatalf("An unexpected RefImplDeleteBody() error: %q", err.Error()) + } + + req := NewDeleteRequest(defaultSpace) + req.Index("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestDeleteRequestSetters(t *testing.T) { key := []interface{}{uint(923)} var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplDeleteBody(refEnc, validSpace, validIndex, key) + err := RefImplDeleteBody(refEnc, &resolver, validSpace, validIndex, key) if err != nil { t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) return @@ -519,7 +682,8 @@ func TestUpdateRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplUpdateBody(refEnc, validSpace, defaultIndex, []interface{}{}, []Op{}) + err := RefImplUpdateBody(refEnc, &resolver, validSpace, defaultIndex, + []interface{}{}, []Op{}) if err != nil { t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) return @@ -529,13 +693,46 @@ func TestUpdateRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestUpdateRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplUpdateBody(refEnc, &resolver, "valid", defaultIndex, + []interface{}{}, []Op{}) + if err != nil { + t.Fatalf("An unexpected RefImplUpdateBody() error: %q", err.Error()) + } + + req := NewUpdateRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestUpdateRequestIndexByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplUpdateBody(refEnc, &resolver, defaultSpace, "valid", + []interface{}{}, []Op{}) + if err != nil { + t.Fatalf("An unexpected RefImplUpdateBody() error: %q", err.Error()) + } + + req := NewUpdateRequest(defaultSpace) + req.Index("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestUpdateRequestSetters(t *testing.T) { key := []interface{}{uint(44)} refOps, reqOps := getTestOps() var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplUpdateBody(refEnc, validSpace, validIndex, key, refOps) + err := RefImplUpdateBody(refEnc, &resolver, validSpace, validIndex, key, refOps) if err != nil { t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) return @@ -552,7 +749,7 @@ func TestUpsertRequestDefaultValues(t *testing.T) { var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplUpsertBody(refEnc, validSpace, []interface{}{}, []Op{}) + err := RefImplUpsertBody(refEnc, &resolver, validSpace, []interface{}{}, []Op{}) if err != nil { t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) return @@ -562,13 +759,28 @@ func TestUpsertRequestDefaultValues(t *testing.T) { assertBodyEqual(t, refBuf.Bytes(), req) } +func TestUpsertRequestSpaceByName(t *testing.T) { + var refBuf bytes.Buffer + + resolver.nameUseSupported = true + + refEnc := msgpack.NewEncoder(&refBuf) + err := RefImplUpsertBody(refEnc, &resolver, "valid", []interface{}{}, []Op{}) + if err != nil { + t.Fatalf("An unexpected RefImplUpsertBody() error: %q", err.Error()) + } + + req := NewUpsertRequest("valid") + assertBodyEqual(t, refBuf.Bytes(), req) +} + func TestUpsertRequestSetters(t *testing.T) { tuple := []interface{}{uint(64)} refOps, reqOps := getTestOps() var refBuf bytes.Buffer refEnc := msgpack.NewEncoder(&refBuf) - err := RefImplUpsertBody(refEnc, validSpace, tuple, refOps) + err := RefImplUpsertBody(refEnc, &resolver, validSpace, tuple, refOps) if err != nil { t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) return diff --git a/schema.go b/schema.go index 714f51c5b..f0be7a162 100644 --- a/schema.go +++ b/schema.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" "github.com/vmihailenco/msgpack/v5/msgpcode" ) @@ -41,9 +42,16 @@ func msgpackIsString(code byte) bool { // SchemaResolver is an interface for resolving schema details. type SchemaResolver interface { - // ResolveSpaceIndex returns resolved space and index numbers or an - // error if it cannot be resolved. - ResolveSpaceIndex(s interface{}, i interface{}) (spaceNo, indexNo uint32, err error) + // ResolveSpace returns resolved space number or an error + // if it cannot be resolved. + ResolveSpace(s interface{}) (spaceNo uint32, err error) + // ResolveIndex returns resolved index number or an error + // if it cannot be resolved. + ResolveIndex(i interface{}, spaceNo uint32) (indexNo uint32, err error) + // NamesUseSupported shows if usage of space and index names, instead of + // IDs, is supported. It must return true if + // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES is supported. + NamesUseSupported() bool } // Schema contains information about spaces and indexes. @@ -364,33 +372,27 @@ func (conn *Connection) loadSchema() (err error) { } } + spaceAndIndexNamesSupported := + isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, + conn.serverProtocolInfo.Features) + conn.lockShards() conn.Schema = schema + conn.schemaResolver = &loadedSchemaResolver{ + Schema: schema, + SpaceAndIndexNamesSupported: spaceAndIndexNamesSupported, + } conn.unlockShards() return nil } -// ResolveSpaceIndex tries to resolve space and index numbers. -// Note: s can be a number, string, or an object of Space type. -// Note: i can be a number, string, or an object of Index type. -func (schema *Schema) ResolveSpaceIndex(s interface{}, i interface{}) (uint32, uint32, error) { - var ( - spaceNo, indexNo uint32 - space *Space - index *Index - ok bool - ) +// resolveSpaceNumber tries to resolve a space number. +// Note: at this point, s can be a number, or an object of Space type. +func resolveSpaceNumber(s interface{}) (uint32, error) { + var spaceNo uint32 switch s := s.(type) { - case string: - if schema == nil { - return spaceNo, indexNo, fmt.Errorf("Schema is not loaded") - } - if space, ok = schema.Spaces[s]; !ok { - return spaceNo, indexNo, fmt.Errorf("there is no space with name %s", s) - } - spaceNo = space.Id case uint: spaceNo = uint32(s) case uint64: @@ -419,50 +421,109 @@ func (schema *Schema) ResolveSpaceIndex(s interface{}, i interface{}) (uint32, u panic("unexpected type of space param") } - if i != nil { - switch i := i.(type) { - case string: - if schema == nil { - return spaceNo, indexNo, fmt.Errorf("schema is not loaded") - } - if space == nil { - if space, ok = schema.SpacesById[spaceNo]; !ok { - return spaceNo, indexNo, fmt.Errorf("there is no space with id %d", spaceNo) - } - } - if index, ok = space.Indexes[i]; !ok { - err := fmt.Errorf("space %s has not index with name %s", space.Name, i) - return spaceNo, indexNo, err - } - indexNo = index.Id - case uint: - indexNo = uint32(i) - case uint64: - indexNo = uint32(i) - case uint32: - indexNo = i - case uint16: - indexNo = uint32(i) - case uint8: - indexNo = uint32(i) - case int: - indexNo = uint32(i) - case int64: - indexNo = uint32(i) - case int32: - indexNo = uint32(i) - case int16: - indexNo = uint32(i) - case int8: - indexNo = uint32(i) - case Index: - indexNo = i.Id - case *Index: - indexNo = i.Id - default: - panic("unexpected type of index param") + return spaceNo, nil +} + +// resolveIndexNumber tries to resolve an index number. +// Note: at this point, i can be a number, or an object of Index type. +func resolveIndexNumber(i interface{}) (uint32, error) { + var indexNo uint32 + + switch i := i.(type) { + case uint: + indexNo = uint32(i) + case uint64: + indexNo = uint32(i) + case uint32: + indexNo = i + case uint16: + indexNo = uint32(i) + case uint8: + indexNo = uint32(i) + case int: + indexNo = uint32(i) + case int64: + indexNo = uint32(i) + case int32: + indexNo = uint32(i) + case int16: + indexNo = uint32(i) + case int8: + indexNo = uint32(i) + case Index: + indexNo = i.Id + case *Index: + indexNo = i.Id + default: + panic("unexpected type of index param") + } + + return indexNo, nil +} + +type loadedSchemaResolver struct { + Schema *Schema + // SpaceAndIndexNamesSupported shows if a current Tarantool version supports + // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES. + SpaceAndIndexNamesSupported bool +} + +func (r *loadedSchemaResolver) ResolveSpace(s interface{}) (uint32, error) { + if str, ok := s.(string); ok { + space, ok := r.Schema.Spaces[str] + if !ok { + return 0, fmt.Errorf("there is no space with name %s", s) + } + return space.Id, nil + } + return resolveSpaceNumber(s) +} + +func (r *loadedSchemaResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) { + if i == nil { + return 0, nil + } + if str, ok := i.(string); ok { + space, ok := r.Schema.SpacesById[spaceNo] + if !ok { + return 0, fmt.Errorf("there is no space with id %d", spaceNo) + } + index, ok := space.Indexes[str] + if !ok { + err := fmt.Errorf("space %s has not index with name %s", space.Name, i) + return 0, err } + return index.Id, nil } + return resolveIndexNumber(i) +} + +func (r *loadedSchemaResolver) NamesUseSupported() bool { + return r.SpaceAndIndexNamesSupported +} + +type noSchemaResolver struct { + // SpaceAndIndexNamesSupported shows if a current Tarantool version supports + // iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES. + SpaceAndIndexNamesSupported bool +} + +func (*noSchemaResolver) ResolveSpace(s interface{}) (uint32, error) { + if _, ok := s.(string); ok { + return 0, fmt.Errorf("unable to use an index name " + + "because schema is not loaded") + } + return resolveSpaceNumber(s) +} + +func (*noSchemaResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) { + if _, ok := i.(string); ok { + return 0, fmt.Errorf("unable to use an index name " + + "because schema is not loaded") + } + return resolveIndexNumber(i) +} - return spaceNo, indexNo, nil +func (r *noSchemaResolver) NamesUseSupported() bool { + return r.SpaceAndIndexNamesSupported } diff --git a/settings/request_test.go b/settings/request_test.go index 2438f81cc..b4c537a29 100644 --- a/settings/request_test.go +++ b/settings/request_test.go @@ -16,24 +16,16 @@ import ( type ValidSchemeResolver struct { } -func (*ValidSchemeResolver) ResolveSpaceIndex(s, i interface{}) (uint32, uint32, error) { - var spaceNo, indexNo uint32 - if s == nil { - if s == "_session_settings" { - spaceNo = 380 - } else { - spaceNo = uint32(s.(int)) - } - } else { - spaceNo = 0 - } - if i != nil { - indexNo = uint32(i.(int)) - } else { - indexNo = 0 - } +func (*ValidSchemeResolver) ResolveSpace(s interface{}) (uint32, error) { + return 0, nil +} + +func (*ValidSchemeResolver) ResolveIndex(i interface{}, spaceNo uint32) (uint32, error) { + return 0, nil +} - return spaceNo, indexNo, nil +func (r *ValidSchemeResolver) NamesUseSupported() bool { + return false } var resolver ValidSchemeResolver diff --git a/tarantool_test.go b/tarantool_test.go index 0ad365286..8be963474 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -585,15 +585,10 @@ func BenchmarkClientReplaceParallel(b *testing.B) { conn := test_helpers.ConnectWithValidation(b, server, opts) defer conn.Close() - rSpaceNo, _, err := conn.Schema.ResolveSpaceIndex("test_perf", "secondary") - if err != nil { - b.Fatalf("Space is not resolved: %s", err.Error()) - } - b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _, err := conn.Replace(rSpaceNo, []interface{}{uint(1), "hello", []interface{}{}}) + _, err := conn.Replace("test_perf", []interface{}{uint(1), "hello", []interface{}{}}) if err != nil { b.Error(err) } @@ -605,17 +600,11 @@ func BenchmarkClientLargeSelectParallel(b *testing.B) { conn := test_helpers.ConnectWithValidation(b, server, opts) defer conn.Close() - schema := conn.Schema - rSpaceNo, rIndexNo, err := schema.ResolveSpaceIndex("test_perf", "secondary") - if err != nil { - b.Fatalf("symbolic space and index params not resolved") - } - offset, limit := uint32(0), uint32(1000) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _, err := conn.Select(rSpaceNo, rIndexNo, offset, limit, IterEq, + _, err := conn.Select("test_perf", "secondary", offset, limit, IterEq, []interface{}{"test_name"}) if err != nil { b.Fatal(err) @@ -1889,8 +1878,6 @@ func TestNewPreparedFromResponse(t *testing.T) { } func TestSchema(t *testing.T) { - var err error - conn := test_helpers.ConnectWithValidation(t, server, opts) defer conn.Close() @@ -2035,32 +2022,6 @@ func TestSchema(t *testing.T) { (ifield2.Type != "STR" && ifield2.Type != "string") { t.Errorf("index field has incorrect Type '%s'", ifield2.Type) } - - var rSpaceNo, rIndexNo uint32 - rSpaceNo, rIndexNo, err = schema.ResolveSpaceIndex(616, 3) - if err != nil || rSpaceNo != 616 || rIndexNo != 3 { - t.Errorf("numeric space and index params not resolved as-is") - } - rSpaceNo, _, err = schema.ResolveSpaceIndex(616, nil) - if err != nil || rSpaceNo != 616 { - t.Errorf("numeric space param not resolved as-is") - } - rSpaceNo, rIndexNo, err = schema.ResolveSpaceIndex("schematest", "secondary") - if err != nil || rSpaceNo != 616 || rIndexNo != 3 { - t.Errorf("symbolic space and index params not resolved") - } - rSpaceNo, _, err = schema.ResolveSpaceIndex("schematest", nil) - if err != nil || rSpaceNo != 616 { - t.Errorf("symbolic space param not resolved") - } - _, _, err = schema.ResolveSpaceIndex("schematest22", "secondary") - if err == nil { - t.Errorf("ResolveSpaceIndex didn't returned error with not existing space name") - } - _, _, err = schema.ResolveSpaceIndex("schematest", "secondary22") - if err == nil { - t.Errorf("ResolveSpaceIndex didn't returned error with not existing index name") - } } func TestSchema_IsNullable(t *testing.T) { @@ -3302,6 +3263,7 @@ func TestConnectionProtocolInfoSupported(t *testing.T) { 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, }, }) @@ -3420,6 +3382,7 @@ func TestConnectionProtocolInfoUnsupported(t *testing.T) { 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, }, }) From d732901e8f8665bde13d3cd426d9acd1b3fe8499 Mon Sep 17 00:00:00 2001 From: DerekBum Date: Thu, 9 Nov 2023 15:11:11 +0300 Subject: [PATCH 2/3] test: more correct use of the `Fatalf` in `request_test.go` Replaced `t.Errorf` + `return` by `t.Fatalf`. This made all tests in the file follow the same code style. Part of #338 --- request_test.go | 102 ++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 68 deletions(-) diff --git a/request_test.go b/request_test.go index f5968b738..7c2e5e514 100644 --- a/request_test.go +++ b/request_test.go @@ -387,8 +387,7 @@ func TestPingRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplPingBody(refEnc) if err != nil { - t.Errorf("An unexpected RefImplPingBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplPingBody() error: %q", err.Error()) } req := NewPingRequest() @@ -402,8 +401,7 @@ func TestSelectRequestDefaultValues(t *testing.T) { err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterAll, []interface{}{}, nil, false) if err != nil { - t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) - return + t.Fatalf("An unexpected RefImplSelectBody() error %q", err.Error()) } req := NewSelectRequest(validSpace) @@ -451,8 +449,7 @@ func TestSelectRequestDefaultIteratorEqIfKey(t *testing.T) { err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, IterEq, key, nil, false) if err != nil { - t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) - return + t.Fatalf("An unexpected RefImplSelectBody() error %q", err.Error()) } req := NewSelectRequest(validSpace). @@ -469,8 +466,7 @@ func TestSelectRequestIteratorNotChangedIfKey(t *testing.T) { err := RefImplSelectBody(refEnc, &resolver, validSpace, defaultIndex, 0, 0xFFFFFFFF, iter, key, nil, false) if err != nil { - t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) - return + t.Fatalf("An unexpected RefImplSelectBody() error %q", err.Error()) } req := NewSelectRequest(validSpace). @@ -492,16 +488,14 @@ func TestSelectRequestSetters(t *testing.T) { err := RefImplSelectBody(refEncAfterBytes, &resolver, validSpace, validIndex, offset, limit, iter, key, afterBytes, true) if err != nil { - t.Errorf("An unexpected RefImplSelectBody() error %s", err) - return + t.Fatalf("An unexpected RefImplSelectBody() error %s", err) } refEncAfterKey := msgpack.NewEncoder(&refBufAfterKey) err = RefImplSelectBody(refEncAfterKey, &resolver, validSpace, validIndex, offset, limit, iter, key, afterKey, true) if err != nil { - t.Errorf("An unexpected RefImplSelectBody() error %s", err) - return + t.Fatalf("An unexpected RefImplSelectBody() error %s", err) } reqAfterBytes := NewSelectRequest(validSpace). @@ -531,8 +525,7 @@ func TestInsertRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplInsertBody(refEnc, &resolver, validSpace, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplInsertBody() error: %q", err.Error()) } req := NewInsertRequest(validSpace) @@ -561,8 +554,7 @@ func TestInsertRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplInsertBody(refEnc, &resolver, validSpace, tuple) if err != nil { - t.Errorf("An unexpected RefImplInsertBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplInsertBody() error: %q", err.Error()) } req := NewInsertRequest(validSpace). @@ -576,8 +568,7 @@ func TestReplaceRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplReplaceBody(refEnc, &resolver, validSpace, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplReplaceBody() error: %q", err.Error()) } req := NewReplaceRequest(validSpace) @@ -592,8 +583,7 @@ func TestReplaceRequestSpaceByName(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplReplaceBody(refEnc, &resolver, "valid", []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplReplaceBody() error: %q", err.Error()) } req := NewReplaceRequest("valid") @@ -607,8 +597,7 @@ func TestReplaceRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplReplaceBody(refEnc, &resolver, validSpace, tuple) if err != nil { - t.Errorf("An unexpected RefImplReplaceBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplReplaceBody() error: %q", err.Error()) } req := NewReplaceRequest(validSpace). @@ -622,8 +611,7 @@ func TestDeleteRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplDeleteBody(refEnc, &resolver, validSpace, defaultIndex, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplDeleteBody() error: %q", err.Error()) } req := NewDeleteRequest(validSpace) @@ -668,8 +656,7 @@ func TestDeleteRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplDeleteBody(refEnc, &resolver, validSpace, validIndex, key) if err != nil { - t.Errorf("An unexpected RefImplDeleteBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplDeleteBody() error: %q", err.Error()) } req := NewDeleteRequest(validSpace). @@ -685,8 +672,7 @@ func TestUpdateRequestDefaultValues(t *testing.T) { err := RefImplUpdateBody(refEnc, &resolver, validSpace, defaultIndex, []interface{}{}, []Op{}) if err != nil { - t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplUpdateBody() error: %q", err.Error()) } req := NewUpdateRequest(validSpace) @@ -734,8 +720,7 @@ func TestUpdateRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplUpdateBody(refEnc, &resolver, validSpace, validIndex, key, refOps) if err != nil { - t.Errorf("An unexpected RefImplUpdateBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplUpdateBody() error: %q", err.Error()) } req := NewUpdateRequest(validSpace). @@ -751,8 +736,7 @@ func TestUpsertRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplUpsertBody(refEnc, &resolver, validSpace, []interface{}{}, []Op{}) if err != nil { - t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplUpsertBody() error: %q", err.Error()) } req := NewUpsertRequest(validSpace) @@ -782,8 +766,7 @@ func TestUpsertRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplUpsertBody(refEnc, &resolver, validSpace, tuple, refOps) if err != nil { - t.Errorf("An unexpected RefImplUpsertBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplUpsertBody() error: %q", err.Error()) } req := NewUpsertRequest(validSpace). @@ -798,8 +781,7 @@ func TestCallRequestsDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplCallBody(refEnc, validExpr, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCallBody() error: %q", err.Error()) } req := NewCallRequest(validExpr) @@ -817,8 +799,7 @@ func TestCallRequestsSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplCallBody(refEnc, validExpr, args) if err != nil { - t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCallBody() error: %q", err.Error()) } req := NewCallRequest(validExpr). @@ -838,8 +819,7 @@ func TestEvalRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplEvalBody(refEnc, validExpr, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplEvalBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplEvalBody() error: %q", err.Error()) } req := NewEvalRequest(validExpr) @@ -853,8 +833,7 @@ func TestEvalRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplEvalBody(refEnc, validExpr, args) if err != nil { - t.Errorf("An unexpected RefImplEvalBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplEvalBody() error: %q", err.Error()) } req := NewEvalRequest(validExpr). @@ -868,8 +847,7 @@ func TestExecuteRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplExecuteBody(refEnc, validExpr, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplExecuteBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplExecuteBody() error: %q", err.Error()) } req := NewExecuteRequest(validExpr) @@ -883,8 +861,7 @@ func TestExecuteRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplExecuteBody(refEnc, validExpr, args) if err != nil { - t.Errorf("An unexpected RefImplExecuteBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplExecuteBody() error: %q", err.Error()) } req := NewExecuteRequest(validExpr). @@ -898,8 +875,7 @@ func TestPrepareRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplPrepareBody(refEnc, validExpr) if err != nil { - t.Errorf("An unexpected RefImplPrepareBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplPrepareBody() error: %q", err.Error()) } req := NewPrepareRequest(validExpr) @@ -912,8 +888,7 @@ func TestUnprepareRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplUnprepareBody(refEnc, *validStmt) if err != nil { - t.Errorf("An unexpected RefImplUnprepareBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplUnprepareBody() error: %q", err.Error()) } req := NewUnprepareRequest(validStmt) @@ -928,8 +903,7 @@ func TestExecutePreparedRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplExecutePreparedBody(refEnc, *validStmt, args) if err != nil { - t.Errorf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) } req := NewExecutePreparedRequest(validStmt). @@ -944,8 +918,7 @@ func TestExecutePreparedRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplExecutePreparedBody(refEnc, *validStmt, []interface{}{}) if err != nil { - t.Errorf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplExecutePreparedBody() error: %q", err.Error()) } req := NewExecutePreparedRequest(validStmt) @@ -959,8 +932,7 @@ func TestBeginRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplBeginBody(refEnc, defaultIsolationLevel, defaultTimeout) if err != nil { - t.Errorf("An unexpected RefImplBeginBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplBeginBody() error: %q", err.Error()) } req := NewBeginRequest() @@ -973,8 +945,7 @@ func TestBeginRequestSetters(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplBeginBody(refEnc, ReadConfirmedLevel, validTimeout) if err != nil { - t.Errorf("An unexpected RefImplBeginBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplBeginBody() error: %q", err.Error()) } req := NewBeginRequest().TxnIsolation(ReadConfirmedLevel).Timeout(validTimeout) @@ -987,8 +958,7 @@ func TestCommitRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplCommitBody(refEnc) if err != nil { - t.Errorf("An unexpected RefImplCommitBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCommitBody() error: %q", err.Error()) } req := NewCommitRequest() @@ -1001,8 +971,7 @@ func TestRollbackRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplRollbackBody(refEnc) if err != nil { - t.Errorf("An unexpected RefImplRollbackBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplRollbackBody() error: %q", err.Error()) } req := NewRollbackRequest() @@ -1016,8 +985,7 @@ func TestBroadcastRequestDefaultValues(t *testing.T) { expectedArgs := []interface{}{validKey} err := RefImplCallBody(refEnc, "box.broadcast", expectedArgs) if err != nil { - t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCallBody() error: %q", err.Error()) } req := NewBroadcastRequest(validKey) @@ -1032,8 +1000,7 @@ func TestBroadcastRequestSetters(t *testing.T) { expectedArgs := []interface{}{validKey, value} err := RefImplCallBody(refEnc, "box.broadcast", expectedArgs) if err != nil { - t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCallBody() error: %q", err.Error()) } req := NewBroadcastRequest(validKey).Value(value) @@ -1046,8 +1013,7 @@ func TestWatchOnceRequestDefaultValues(t *testing.T) { refEnc := msgpack.NewEncoder(&refBuf) err := RefImplWatchOnceBody(refEnc, validKey) if err != nil { - t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) - return + t.Fatalf("An unexpected RefImplCallBody() error: %q", err.Error()) } req := NewWatchOnceRequest(validKey) From b7dd4d80a4b8936f7b1e04b3a1ca3427bc334252 Mon Sep 17 00:00:00 2001 From: DerekBum Date: Thu, 9 Nov 2023 15:57:53 +0300 Subject: [PATCH 3/3] ci: update Tarantool EE 1.10, 2.10 and 2.11 versions Update Tarantool EE version 1.10.11 to 1.10.15, 2.10.0 to 2.10.8 and 2.11.0 to 2.11.1. This was done because of the one flacking test: https://github.com/tarantool/go-tarantool/actions/runs/6805504621/job/18505152412 Closes #338 --- .github/workflows/testing.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 66d0b7063..c21344e27 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -138,17 +138,20 @@ jobs: strategy: fail-fast: false matrix: + sdk-path: + - 'release/linux/x86_64/1.10/' sdk-version: - - 'bundle-1.10.11-0-gf0b0e7ecf-r470' + - 'sdk-1.10.15-0-r598' coveralls: [false] fuzzing: [false] ssl: [false] include: - - sdk-version: 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64' + - sdk-path: 'release/linux/x86_64/2.10/' + sdk-version: 'sdk-gc64-2.10.8-0-r598.linux.x86_64' coveralls: false ssl: true - sdk-path: 'release/linux/x86_64/2.11/' - sdk-version: 'sdk-gc64-2.11.0-0-r577.linux.x86_64' + sdk-version: 'sdk-gc64-2.11.1-0-r598.linux.x86_64' coveralls: true ssl: true