From 4ebcb5f1e07de3d2038781ae6f2b1d34d5298cfd Mon Sep 17 00:00:00 2001 From: DerekBum Date: Thu, 7 Dec 2023 18:20:57 +0300 Subject: [PATCH 1/4] api: change `Response` type to interface This commit creates a new `Response` interface. But still, only one `Response` implementation exists: `ConnResponse`. Custom responses (including mocks) are expected to implement this interface. Create a `Response` interface and its implementation `ConnResponse`. For the `Future` method `Get` now returns response data. To get the actual response new method `GetResponse` is added. `IsPush()` method is added to the response iterator. It returns the information if the current response is a `Push`. Right now it does not have a lot of usage, but it will be crucial once we create a separate response for pushes. Part of #237 --- CHANGELOG.md | 10 + README.md | 22 +- box_error_test.go | 18 +- connection.go | 26 +- connector.go | 24 +- const.go | 3 +- crud/tarantool_test.go | 63 +-- datetime/datetime_test.go | 50 +- datetime/example_test.go | 15 +- datetime/interval_test.go | 4 +- decimal/decimal_test.go | 50 +- decimal/example_test.go | 8 +- dial.go | 39 +- dial_test.go | 3 +- example_custom_unpacking_test.go | 6 +- example_test.go | 305 +++++----- future.go | 53 +- future_test.go | 61 +- header.go | 10 + pool/connection_pool.go | 35 +- pool/connection_pool_test.go | 388 +++++++------ pool/connector.go | 24 +- pool/connector_test.go | 26 +- pool/example_test.go | 83 ++- pool/pooler.go | 24 +- prepared.go | 12 +- queue/queue.go | 24 +- request.go | 50 +- response.go | 196 ++++--- response_it.go | 4 +- settings/example_test.go | 8 +- settings/tarantool_test.go | 345 +++++------- shutdown_test.go | 6 +- tarantool_test.go | 934 +++++++++++++------------------ test_helpers/main.go | 4 +- test_helpers/pool_helper.go | 18 +- uuid/example_test.go | 5 +- uuid/uuid_test.go | 21 +- 38 files changed, 1374 insertions(+), 1603 deletions(-) create mode 100644 header.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 36f5c08db..6b362a4d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. in requests instead of their IDs. - `GetSchema` function to get the actual schema (#7) - Support connection via an existing socket fd (#321) +- `Header` struct for the response header (#237). It can be accessed via + `Header()` method of the `Response` interface. ### Changed @@ -67,6 +69,13 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. it (#321) - Rename `pool.GetPoolInfo` to `pool.GetInfo`. Change return type to `map[string]ConnectionInfo` (#321) +- `Response` is now an interface (#237) +- All responses are now implementations of the `Response` interface (#237) +- `IsPush()` method is added to the response iterator (#237). It returns + the information if the current response is a `PushResponse`. + `PushCode` constant is removed. +- Method `Get` for `Future` now returns response data (#237). To get the actual + response new `GetResponse` method has been added. ### Deprecated @@ -89,6 +98,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - IPROTO constants (#158) - Code() method from the Request interface (#158) - `Schema` field from the `Connection` struct (#7) +- `PushCode` constant (#237) ### Fixed diff --git a/README.md b/README.md index b1d47ee84..a08ce1794 100644 --- a/README.md +++ b/README.md @@ -128,12 +128,13 @@ func main() { if err != nil { fmt.Println("Connection refused:", err) } - resp, err := conn.Do(tarantool.NewInsertRequest(999). + data, err := conn.Do(tarantool.NewInsertRequest(999). Tuple([]interface{}{99999, "BB"}), ).Get() if err != nil { fmt.Println("Error", err) - fmt.Println("Code", resp.Code) + } else { + fmt.Printf("Data: %v", data) } } ``` @@ -241,7 +242,9 @@ of the requests is an array instead of array of arrays. #### IPROTO constants -IPROTO constants have been moved to a separate package [go-iproto](https://github.com/tarantool/go-iproto). +* IPROTO constants have been moved to a separate package [go-iproto](https://github.com/tarantool/go-iproto). +* `PushCode` constant is removed. To check whether the current response is + a push response, use `IsPush()` method of the response iterator instead. #### Request changes @@ -255,6 +258,19 @@ needs to be passed instead. * `UpdateRequest` and `UpsertRequest` structs no longer accept `interface{}` for an `ops` field. `*Operations` needs to be used instead. +#### Response changes + +* `Response` is now an interface. +* Response header stored in a new `Header` struct. It could be accessed via + `Header()` method. +* `ResponseIterator` interface now has `IsPush()` method. + It returns true if the current response is a push response. + +#### Future changes + +* Method `Get` now returns response data instead of the actual response. +* New method `GetResponse` added to get an actual response. + #### Connect function `connection.Connect` no longer return non-working connection objects. This function diff --git a/box_error_test.go b/box_error_test.go index b08b6cc52..6839a1477 100644 --- a/box_error_test.go +++ b/box_error_test.go @@ -306,9 +306,11 @@ func TestErrorTypeEval(t *testing.T) { t.Run(name, func(t *testing.T) { resp, err := conn.Eval("return ...", []interface{}{&testcase.tuple.val}) require.Nil(t, err) - require.NotNil(t, resp.Data) - require.Equal(t, len(resp.Data), 1) - actual, ok := resp.Data[0].(*BoxError) + data, err := resp.Decode() + require.Nil(t, err) + require.NotNil(t, data) + require.Equal(t, len(data), 1) + actual, ok := data[0].(*BoxError) require.Truef(t, ok, "Response data has valid type") require.Equal(t, testcase.tuple.val, *actual) }) @@ -436,15 +438,17 @@ func TestErrorTypeSelect(t *testing.T) { _, err := conn.Eval(insertEval, []interface{}{}) require.Nilf(t, err, "Tuple has been successfully inserted") - var resp *Response + var resp Response var offset uint32 = 0 var limit uint32 = 1 resp, err = conn.Select(space, index, offset, limit, IterEq, []interface{}{testcase.tuple.pk}) require.Nil(t, err) - require.NotNil(t, resp.Data) - require.Equalf(t, len(resp.Data), 1, "Exactly one tuple had been found") - tpl, ok := resp.Data[0].([]interface{}) + data, err := resp.Decode() + require.Nil(t, err) + require.NotNil(t, data) + require.Equalf(t, len(data), 1, "Exactly one tuple had been found") + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "Tuple has valid type") require.Equal(t, testcase.tuple.pk, tpl[0]) actual, ok := tpl[1].(*BoxError) diff --git a/connection.go b/connection.go index bf9f1554e..1dab74187 100644 --- a/connection.go +++ b/connection.go @@ -97,9 +97,9 @@ func (d defaultLogger) Report(event ConnLogKind, conn *Connection, v ...interfac log.Printf("tarantool: last reconnect to %s failed: %s, giving it up", conn.Addr(), err) case LogUnexpectedResultId: - resp := v[0].(*Response) + header := v[0].(Header) log.Printf("tarantool: connection %s got unexpected resultId (%d) in response", - conn.Addr(), resp.RequestId) + conn.Addr(), header.RequestId) case LogWatchEventReadFailed: err := v[0].(error) log.Printf("tarantool: unable to parse watch event: %s", err) @@ -807,8 +807,8 @@ func (conn *Connection) reader(r io.Reader, c Conn) { conn.reconnect(err, c) return } - resp := &Response{buf: smallBuf{b: respBytes}} - err = resp.decodeHeader(conn.dec) + buf := smallBuf{b: respBytes} + header, err := decodeHeader(conn.dec, &buf) if err != nil { err = ClientError{ ErrProtocolError, @@ -818,8 +818,9 @@ func (conn *Connection) reader(r io.Reader, c Conn) { return } + resp := &ConnResponse{header: header, buf: buf} var fut *Future = nil - if iproto.Type(resp.Code) == iproto.IPROTO_EVENT { + if iproto.Type(header.Code) == iproto.IPROTO_EVENT { if event, err := readWatchEvent(&resp.buf); err == nil { events <- event } else { @@ -830,19 +831,19 @@ func (conn *Connection) reader(r io.Reader, c Conn) { conn.opts.Logger.Report(LogWatchEventReadFailed, conn, err) } continue - } else if resp.Code == PushCode { - if fut = conn.peekFuture(resp.RequestId); fut != nil { + } else if header.Code == uint32(iproto.IPROTO_CHUNK) { + if fut = conn.peekFuture(header.RequestId); fut != nil { fut.AppendPush(resp) } } else { - if fut = conn.fetchFuture(resp.RequestId); fut != nil { + if fut = conn.fetchFuture(header.RequestId); fut != nil { fut.SetResponse(resp) conn.markDone(fut) } } if fut == nil { - conn.opts.Logger.Report(LogUnexpectedResultId, conn, resp) + conn.opts.Logger.Report(LogUnexpectedResultId, conn, header) } } } @@ -1052,10 +1053,7 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) { if req.Async() { if fut = conn.fetchFuture(reqid); fut != nil { - resp := &Response{ - RequestId: reqid, - Code: OkCode, - } + resp := &ConnResponse{} fut.SetResponse(resp) conn.markDone(fut) } @@ -1236,7 +1234,7 @@ func (conn *Connection) SetSchema(s Schema) { // NewPrepared passes a sql statement to Tarantool for preparation synchronously. func (conn *Connection) NewPrepared(expr string) (*Prepared, error) { req := NewPrepareRequest(expr) - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() if err != nil { return nil, err } diff --git a/connector.go b/connector.go index 9536116d7..b7f5affed 100644 --- a/connector.go +++ b/connector.go @@ -13,41 +13,41 @@ type Connector interface { // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. - Ping() (*Response, error) + Ping() (Response, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. Select(space, index interface{}, offset, limit uint32, iterator Iter, - key interface{}) (*Response, error) + key interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. - Insert(space interface{}, tuple interface{}) (*Response, error) + Insert(space interface{}, tuple interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a ReplicaRequest object + Do() instead. - Replace(space interface{}, tuple interface{}) (*Response, error) + Replace(space interface{}, tuple interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. - Delete(space, index interface{}, key interface{}) (*Response, error) + Delete(space, index interface{}, key interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. - Update(space, index interface{}, key interface{}, ops *Operations) (*Response, error) + Update(space, index interface{}, key interface{}, ops *Operations) (Response, error) // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. - Upsert(space interface{}, tuple interface{}, ops *Operations) (*Response, error) + Upsert(space interface{}, tuple interface{}, ops *Operations) (Response, error) // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. - Call(functionName string, args interface{}) (*Response, error) + Call(functionName string, args interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. - Call16(functionName string, args interface{}) (*Response, error) + Call16(functionName string, args interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. - Call17(functionName string, args interface{}) (*Response, error) + Call17(functionName string, args interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. - Eval(expr string, args interface{}) (*Response, error) + Eval(expr string, args interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. - Execute(expr string, args interface{}) (*Response, error) + Execute(expr string, args interface{}) (Response, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. diff --git a/const.go b/const.go index 62650b5be..ede4c988d 100644 --- a/const.go +++ b/const.go @@ -9,6 +9,5 @@ const ( ) const ( - OkCode = uint32(iproto.IPROTO_OK) - PushCode = uint32(iproto.IPROTO_CHUNK) + OkCode = uint32(iproto.IPROTO_OK) ) diff --git a/crud/tarantool_test.go b/crud/tarantool_test.go index dfc8d064e..8ee28cf09 100644 --- a/crud/tarantool_test.go +++ b/crud/tarantool_test.go @@ -517,39 +517,32 @@ func testSelectGeneratedData(t *testing.T, conn tarantool.Connector, Limit(20). Iterator(tarantool.IterGe). Key([]interface{}{uint(1010)}) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != expectedTuplesCount { - t.Fatalf("Response Data len %d != %d", len(resp.Data), expectedTuplesCount) + if len(data) != expectedTuplesCount { + t.Fatalf("Response Data len %d != %d", len(data), expectedTuplesCount) } } func testCrudRequestCheck(t *testing.T, req tarantool.Request, - resp *tarantool.Response, err error, expectedLen int) { + data []interface{}, err error, expectedLen int) { t.Helper() if err != nil { t.Fatalf("Failed to Do CRUD request: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Do CRUD request") - } - - if len(resp.Data) < expectedLen { + if len(data) < expectedLen { t.Fatalf("Response Body len < %#v, actual len %#v", - expectedLen, len(resp.Data)) + expectedLen, len(data)) } // resp.Data[0] - CRUD res. // resp.Data[1] - CRUD err. - if expectedLen >= 2 && resp.Data[1] != nil { - if crudErr, err := getCrudError(req, resp.Data[1]); err != nil { + if expectedLen >= 2 && data[1] != nil { + if crudErr, err := getCrudError(req, data[1]); err != nil { t.Fatalf("Failed to get CRUD error: %#v", err) } else if crudErr != nil { t.Fatalf("Failed to perform CRUD request on CRUD side: %#v", crudErr) @@ -569,8 +562,8 @@ func TestCrudGenerateData(t *testing.T) { conn.Do(req).Get() } - resp, err := conn.Do(testCase.req).Get() - testCrudRequestCheck(t, testCase.req, resp, + data, err := conn.Do(testCase.req).Get() + testCrudRequestCheck(t, testCase.req, data, err, testCase.expectedRespLen) testSelectGeneratedData(t, conn, testCase.expectedTuplesCount) @@ -591,8 +584,8 @@ func TestCrudProcessData(t *testing.T) { for _, testCase := range testProcessDataCases { t.Run(testCase.name, func(t *testing.T) { testCrudRequestPrepareData(t, conn) - resp, err := conn.Do(testCase.req).Get() - testCrudRequestCheck(t, testCase.req, resp, + data, err := conn.Do(testCase.req).Get() + testCrudRequestCheck(t, testCase.req, data, err, testCase.expectedRespLen) for i := 1010; i < 1020; i++ { req := tarantool.NewDeleteRequest(spaceName). @@ -623,8 +616,8 @@ func TestCrudUpdateSplice(t *testing.T) { Opts(simpleOperationOpts) testCrudRequestPrepareData(t, conn) - resp, err := conn.Do(req).Get() - testCrudRequestCheck(t, req, resp, + data, err := conn.Do(req).Get() + testCrudRequestCheck(t, req, data, err, 2) } @@ -648,8 +641,8 @@ func TestCrudUpsertSplice(t *testing.T) { Opts(simpleOperationOpts) testCrudRequestPrepareData(t, conn) - resp, err := conn.Do(req).Get() - testCrudRequestCheck(t, req, resp, + data, err := conn.Do(req).Get() + testCrudRequestCheck(t, req, data, err, 2) } @@ -673,8 +666,8 @@ func TestCrudUpsertObjectSplice(t *testing.T) { Opts(simpleOperationOpts) testCrudRequestPrepareData(t, conn) - resp, err := conn.Do(req).Get() - testCrudRequestCheck(t, req, resp, + data, err := conn.Do(req).Get() + testCrudRequestCheck(t, req, data, err, 2) } @@ -719,11 +712,11 @@ func TestUnflattenRows(t *testing.T) { req := crud.MakeReplaceRequest(spaceName). Tuple(tuple). Opts(simpleOperationOpts) - resp, err := conn.Do(req).Get() - testCrudRequestCheck(t, req, resp, err, 2) + data, err := conn.Do(req).Get() + testCrudRequestCheck(t, req, data, err, 2) - if res, ok = resp.Data[0].(map[interface{}]interface{}); !ok { - t.Fatalf("Unexpected CRUD result: %#v", resp.Data[0]) + if res, ok = data[0].(map[interface{}]interface{}); !ok { + t.Fatalf("Unexpected CRUD result: %#v", data[0]) } if rawMetadata, ok := res["metadata"]; !ok { @@ -1293,21 +1286,21 @@ func TestNoreturnOption(t *testing.T) { conn.Do(req).Get() } - resp, err := conn.Do(testCase.req).Get() + data, err := conn.Do(testCase.req).Get() if err != nil { t.Fatalf("Failed to Do CRUD request: %s", err) } - if len(resp.Data) == 0 { + if len(data) == 0 { t.Fatalf("Expected explicit nil") } - if resp.Data[0] != nil { - t.Fatalf("Expected nil result, got %v", resp.Data[0]) + if data[0] != nil { + t.Fatalf("Expected nil result, got %v", data[0]) } - if len(resp.Data) >= 2 && resp.Data[1] != nil { - t.Fatalf("Expected no returned errors, got %v", resp.Data[1]) + if len(data) >= 2 && data[1] != nil { + t.Fatalf("Expected no returned errors, got %v", data[1]) } for i := 1010; i < 1020; i++ { diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 630d5f062..8be50e0e7 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -405,12 +405,12 @@ func TestDatetimeTarantoolInterval(t *testing.T) { func(t *testing.T) { req := NewCallRequest("call_datetime_interval"). Args([]interface{}{dti, dtj}) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Unable to call call_datetime_interval: %s", err) } ival := dti.Interval(dtj) - ret := resp.Data[0].(Interval) + ret := data[0].(Interval) if !reflect.DeepEqual(ival, ret) { t.Fatalf("%v != %v", ival, ret) } @@ -540,13 +540,13 @@ func TestCustomTimezone(t *testing.T) { } req := NewReplaceRequest(spaceTuple1).Tuple([]interface{}{dt, "payload"}) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Datetime replace failed %s", err.Error()) } - assertDatetimeIsEqual(t, resp.Data, tm) + assertDatetimeIsEqual(t, data, tm) - tpl := resp.Data[0].([]interface{}) + tpl := data[0].([]interface{}) if respDt, ok := tpl[0].(Datetime); ok { zone := respDt.ToTime().Location().String() _, offset := respDt.ToTime().Zone() @@ -592,25 +592,19 @@ func tupleInsertSelectDelete(t *testing.T, conn *Connection, tm time.Time) { Limit(limit). Iterator(IterEq). Key([]interface{}{dt}) - resp, err := conn.Do(sel).Get() + data, err := conn.Do(sel).Get() if err != nil { t.Fatalf("Datetime select failed: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - assertDatetimeIsEqual(t, resp.Data, tm) + assertDatetimeIsEqual(t, data, tm) // Delete tuple with datetime. del := NewDeleteRequest(spaceTuple1).Index(index).Key([]interface{}{dt}) - resp, err = conn.Do(del).Get() + data, err = conn.Do(del).Get() if err != nil { t.Fatalf("Datetime delete failed: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Delete") - } - assertDatetimeIsEqual(t, resp.Data, tm) + assertDatetimeIsEqual(t, data, tm) } var datetimeSample = []struct { @@ -747,28 +741,22 @@ func TestDatetimeReplace(t *testing.T) { t.Fatalf("Unable to create Datetime from %s: %s", tm, err) } rep := NewReplaceRequest(spaceTuple1).Tuple([]interface{}{dt, "payload"}) - resp, err := conn.Do(rep).Get() + data, err := conn.Do(rep).Get() if err != nil { t.Fatalf("Datetime replace failed: %s", err) } - if resp == nil { - t.Fatalf("Response is nil after Replace") - } - assertDatetimeIsEqual(t, resp.Data, tm) + assertDatetimeIsEqual(t, data, tm) sel := NewSelectRequest(spaceTuple1). Index(index). Limit(1). Iterator(IterEq). Key([]interface{}{dt}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() if err != nil { t.Fatalf("Datetime select failed: %s", err) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - assertDatetimeIsEqual(t, resp.Data, tm) + assertDatetimeIsEqual(t, data, tm) // Delete tuple with datetime. del := NewDeleteRequest(spaceTuple1).Index(index).Key([]interface{}{dt}) @@ -923,15 +911,15 @@ func TestCustomEncodeDecodeTuple1(t *testing.T) { }, } rep := NewReplaceRequest(spaceTuple2).Tuple(&tuple) - resp, err := conn.Do(rep).Get() - if err != nil || resp.Code != 0 { + data, err := conn.Do(rep).Get() + if err != nil { t.Fatalf("Failed to replace: %s", err.Error()) } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Body len != 1") } - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) if !ok { t.Fatalf("Unexpected body of Replace") } @@ -1033,11 +1021,11 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) { Limit(1). Iterator(IterEq). Key([]interface{}{dt}) - resp, errSel := conn.Do(sel).Get() + data, errSel := conn.Do(sel).Get() if errSel != nil { t.Errorf("Failed to Select: %s", errSel.Error()) } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { if val, ok := tpl[0].(Datetime); !ok || !val.ToTime().Equal(tm) { diff --git a/datetime/example_test.go b/datetime/example_test.go index 72d3448c2..ac5f40500 100644 --- a/datetime/example_test.go +++ b/datetime/example_test.go @@ -50,22 +50,21 @@ func Example() { index := "primary" // Replace a tuple with datetime. - resp, err := conn.Do(tarantool.NewReplaceRequest(space). + data, err := conn.Do(tarantool.NewReplaceRequest(space). Tuple([]interface{}{dt}), ).Get() if err != nil { fmt.Printf("Error in replace is %v", err) return } - respDt := resp.Data[0].([]interface{})[0].(Datetime) + respDt := data[0].([]interface{})[0].(Datetime) fmt.Println("Datetime tuple replace") - fmt.Printf("Code: %d\n", resp.Code) fmt.Printf("Data: %v\n", respDt.ToTime()) // Select a tuple with datetime. var offset uint32 = 0 var limit uint32 = 1 - resp, err = conn.Do(tarantool.NewSelectRequest(space). + data, err = conn.Do(tarantool.NewSelectRequest(space). Index(index). Offset(offset). Limit(limit). @@ -76,13 +75,12 @@ func Example() { fmt.Printf("Error in select is %v", err) return } - respDt = resp.Data[0].([]interface{})[0].(Datetime) + respDt = data[0].([]interface{})[0].(Datetime) fmt.Println("Datetime tuple select") - fmt.Printf("Code: %d\n", resp.Code) fmt.Printf("Data: %v\n", respDt.ToTime()) // Delete a tuple with datetime. - resp, err = conn.Do(tarantool.NewDeleteRequest(space). + data, err = conn.Do(tarantool.NewDeleteRequest(space). Index(index). Key([]interface{}{dt}), ).Get() @@ -90,9 +88,8 @@ func Example() { fmt.Printf("Error in delete is %v", err) return } - respDt = resp.Data[0].([]interface{})[0].(Datetime) + respDt = data[0].([]interface{})[0].(Datetime) fmt.Println("Datetime tuple delete") - fmt.Printf("Code: %d\n", resp.Code) fmt.Printf("Data: %v\n", respDt.ToTime()) } diff --git a/datetime/interval_test.go b/datetime/interval_test.go index 4e3cf5ab1..95142fe47 100644 --- a/datetime/interval_test.go +++ b/datetime/interval_test.go @@ -121,12 +121,12 @@ func TestIntervalTarantoolEncoding(t *testing.T) { t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) { req := tarantool.NewCallRequest("call_interval_testdata"). Args([]interface{}{tc}) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Unexpected error: %s", err.Error()) } - ret := resp.Data[0].(Interval) + ret := data[0].(Interval) if !reflect.DeepEqual(ret, tc) { t.Fatalf("Unexpected response: %v, expected %v", ret, tc) } diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go index 14ce05b2a..573daa8f6 100644 --- a/decimal/decimal_test.go +++ b/decimal/decimal_test.go @@ -538,14 +538,11 @@ func TestSelect(t *testing.T) { } ins := NewInsertRequest(space).Tuple([]interface{}{MakeDecimal(number)}) - resp, err := conn.Do(ins).Get() + data, err := conn.Do(ins).Get() if err != nil { t.Fatalf("Decimal insert failed: %s", err) } - if resp == nil { - t.Fatalf("Response is nil after Replace") - } - tupleValueIsDecimal(t, resp.Data, number) + tupleValueIsDecimal(t, data, number) var offset uint32 = 0 var limit uint32 = 1 @@ -555,21 +552,18 @@ func TestSelect(t *testing.T) { Limit(limit). Iterator(IterEq). Key([]interface{}{MakeDecimal(number)}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() if err != nil { t.Fatalf("Decimal select failed: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - tupleValueIsDecimal(t, resp.Data, number) + tupleValueIsDecimal(t, data, number) del := NewDeleteRequest(space).Index(index).Key([]interface{}{MakeDecimal(number)}) - resp, err = conn.Do(del).Get() + data, err = conn.Do(del).Get() if err != nil { t.Fatalf("Decimal delete failed: %s", err) } - tupleValueIsDecimal(t, resp.Data, number) + tupleValueIsDecimal(t, data, number) } func TestUnmarshal_from_decimal_new(t *testing.T) { @@ -591,14 +585,11 @@ func TestUnmarshal_from_decimal_new(t *testing.T) { call := NewEvalRequest("return require('decimal').new(...)"). Args([]interface{}{str}) - resp, err := conn.Do(call).Get() + data, err := conn.Do(call).Get() if err != nil { t.Fatalf("Decimal create failed: %s", err) } - if resp == nil { - t.Fatalf("Response is nil after Call") - } - tupleValueIsDecimal(t, []interface{}{resp.Data}, number) + tupleValueIsDecimal(t, []interface{}{data}, number) }) } } @@ -610,21 +601,18 @@ func assertInsert(t *testing.T, conn *Connection, numString string) { } ins := NewInsertRequest(space).Tuple([]interface{}{MakeDecimal(number)}) - resp, err := conn.Do(ins).Get() + data, err := conn.Do(ins).Get() if err != nil { t.Fatalf("Decimal insert failed: %s", err) } - if resp == nil { - t.Fatalf("Response is nil after Replace") - } - tupleValueIsDecimal(t, resp.Data, number) + tupleValueIsDecimal(t, data, number) del := NewDeleteRequest(space).Index(index).Key([]interface{}{MakeDecimal(number)}) - resp, err = conn.Do(del).Get() + data, err = conn.Do(del).Get() if err != nil { t.Fatalf("Decimal delete failed: %s", err) } - tupleValueIsDecimal(t, resp.Data, number) + tupleValueIsDecimal(t, data, number) } func TestInsert(t *testing.T) { @@ -654,28 +642,22 @@ func TestReplace(t *testing.T) { } rep := NewReplaceRequest(space).Tuple([]interface{}{MakeDecimal(number)}) - respRep, errRep := conn.Do(rep).Get() + dataRep, errRep := conn.Do(rep).Get() if errRep != nil { t.Fatalf("Decimal replace failed: %s", errRep) } - if respRep == nil { - t.Fatalf("Response is nil after Replace") - } - tupleValueIsDecimal(t, respRep.Data, number) + tupleValueIsDecimal(t, dataRep, number) sel := NewSelectRequest(space). Index(index). Limit(1). Iterator(IterEq). Key([]interface{}{MakeDecimal(number)}) - respSel, errSel := conn.Do(sel).Get() + dataSel, errSel := conn.Do(sel).Get() if errSel != nil { t.Fatalf("Decimal select failed: %s", errSel) } - if respSel == nil { - t.Fatalf("Response is nil after Select") - } - tupleValueIsDecimal(t, respSel.Data, number) + tupleValueIsDecimal(t, dataSel, number) } // runTestMain is a body of TestMain function diff --git a/decimal/example_test.go b/decimal/example_test.go index 3f7d4de06..5597590dc 100644 --- a/decimal/example_test.go +++ b/decimal/example_test.go @@ -44,18 +44,14 @@ func Example() { log.Fatalf("Failed to prepare test decimal: %s", err) } - resp, err := client.Do(tarantool.NewReplaceRequest(spaceNo). + data, err := client.Do(tarantool.NewReplaceRequest(spaceNo). Tuple([]interface{}{number}), ).Get() if err != nil { log.Fatalf("Decimal replace failed: %s", err) } - if resp == nil { - log.Fatalf("Response is nil after Replace") - } log.Println("Decimal tuple replace") log.Println("Error", err) - log.Println("Code", resp.Code) - log.Println("Data", resp.Data) + log.Println("Data", data) } diff --git a/dial.go b/dial.go index eae8e1283..37cbe0139 100644 --- a/dial.go +++ b/dial.go @@ -400,19 +400,27 @@ func identify(w writeFlusher, r io.Reader) (ProtocolInfo, error) { resp, err := readResponse(r) if err != nil { - if iproto.Error(resp.Code) == iproto.ER_UNKNOWN_REQUEST_TYPE { - // IPROTO_ID requests are not supported by server. - return info, nil - } - return info, err } + data, err := resp.Decode() + if err != nil { + switch err := err.(type) { + case Error: + if err.Code == iproto.ER_UNKNOWN_REQUEST_TYPE { + // IPROTO_ID requests are not supported by server. + return info, nil + } + return info, err + default: + return info, fmt.Errorf("decode response body error: %w", err) + } + } - if len(resp.Data) == 0 { + if len(data) == 0 { return info, errors.New("unexpected response: no data") } - info, ok := resp.Data[0].(ProtocolInfo) + info, ok := data[0].(ProtocolInfo) if !ok { return info, errors.New("unexpected response: wrong data") } @@ -503,23 +511,14 @@ func readResponse(r io.Reader) (Response, error) { respBytes, err := read(r, lenbuf[:]) if err != nil { - return Response{}, fmt.Errorf("read error: %w", err) + return &ConnResponse{}, fmt.Errorf("read error: %w", err) } - resp := Response{buf: smallBuf{b: respBytes}} - err = resp.decodeHeader(msgpack.NewDecoder(&smallBuf{})) + buf := smallBuf{b: respBytes} + header, err := decodeHeader(msgpack.NewDecoder(&smallBuf{}), &buf) + resp := &ConnResponse{header: header, buf: buf} if err != nil { return resp, fmt.Errorf("decode response header error: %w", err) } - - err = resp.decodeBody() - if err != nil { - switch err.(type) { - case Error: - return resp, err - default: - return resp, fmt.Errorf("decode response body error: %w", err) - } - } return resp, nil } diff --git a/dial_test.go b/dial_test.go index 17f037e00..88a582b07 100644 --- a/dial_test.go +++ b/dial_test.go @@ -303,9 +303,8 @@ func TestConn_ReadWrite(t *testing.T) { 0x80, // Empty map. }, dialer.conn.writebuf.Bytes()) - resp, err := fut.Get() + _, err := fut.Get() assert.Nil(t, err) - assert.NotNil(t, resp) } func TestConn_ContextCancel(t *testing.T) { diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index 316e0086f..a2706f3f6 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -97,13 +97,12 @@ func Example_customUnpacking() { tuple := Tuple2{Cid: 777, Orig: "orig", Members: []Member{{"lol", "", 1}, {"wut", "", 3}}} // Insert a structure itself. initReq := tarantool.NewReplaceRequest(spaceNo).Tuple(&tuple) - resp, err := conn.Do(initReq).Get() + data, err := conn.Do(initReq).Get() if err != nil { log.Fatalf("Failed to insert: %s", err.Error()) return } - fmt.Println("Data", resp.Data) - fmt.Println("Code", resp.Code) + fmt.Println("Data", data) var tuples1 []Tuple2 selectReq := tarantool.NewSelectRequest(spaceNo). @@ -139,7 +138,6 @@ func Example_customUnpacking() { // Output: // Data [[777 orig [[lol 1] [wut 3]]]] - // Code 0 // Tuples (tuples1) [{777 orig [{lol 1} {wut 3}]}] // Tuples (tuples2): [{{} 777 orig [{lol 1} {wut 3}]}] // Tuples (tuples3): [[{{} 221 [{Moscow 34} {Minsk 23} {Kiev 31}]}]] diff --git a/example_test.go b/example_test.go index 6500e18f4..a39b9d5a1 100644 --- a/example_test.go +++ b/example_test.go @@ -149,12 +149,10 @@ func ExamplePingRequest() { defer conn.Close() // Ping a Tarantool instance to check connection. - resp, err := conn.Do(tarantool.NewPingRequest()).Get() - fmt.Println("Ping Code", resp.Code) - fmt.Println("Ping Data", resp.Data) + data, err := conn.Do(tarantool.NewPingRequest()).Get() + fmt.Println("Ping Data", data) fmt.Println("Ping Error", err) // Output: - // Ping Code 0 // Ping Data [] // Ping Error } @@ -181,11 +179,11 @@ func ExamplePingRequest_Context() { req := tarantool.NewPingRequest().Context(ctx) // Ping a Tarantool instance to check connection. - resp, err := conn.Do(req).Get() - fmt.Println("Ping Resp", resp) + data, err := conn.Do(req).Get() + fmt.Println("Ping Resp data", data) fmt.Println("Ping Error", err) // Output: - // Ping Resp + // Ping Resp data [] // Ping Error context is done } @@ -200,7 +198,7 @@ func ExampleSelectRequest() { } key := []interface{}{uint(1111)} - resp, err := conn.Do(tarantool.NewSelectRequest(617). + data, err := conn.Do(tarantool.NewSelectRequest(617). Limit(100). Iterator(tarantool.IterEq). Key(key), @@ -210,7 +208,7 @@ func ExampleSelectRequest() { fmt.Printf("error in select is %v", err) return } - fmt.Printf("response is %#v\n", resp.Data) + fmt.Printf("response is %#v\n", data) var res []Tuple err = conn.Do(tarantool.NewSelectRequest("test"). @@ -236,12 +234,12 @@ func ExampleSelectRequest_spaceAndIndexNames() { req := tarantool.NewSelectRequest(spaceName) req.Index(indexName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -250,21 +248,19 @@ func ExampleInsertRequest() { defer conn.Close() // Insert a new tuple { 31, 1 }. - resp, err := conn.Do(tarantool.NewInsertRequest(spaceNo). + data, err := conn.Do(tarantool.NewInsertRequest(spaceNo). Tuple([]interface{}{uint(31), "test", "one"}), ).Get() fmt.Println("Insert 31") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Insert a new tuple { 32, 1 }. - resp, err = conn.Do(tarantool.NewInsertRequest("test"). + data, err = conn.Do(tarantool.NewInsertRequest("test"). Tuple(&Tuple{Id: 32, Msg: "test", Name: "one"}), ).Get() fmt.Println("Insert 32") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Delete tuple with primary key { 31 }. conn.Do(tarantool.NewDeleteRequest("test"). @@ -279,11 +275,9 @@ func ExampleInsertRequest() { // Output: // Insert 31 // Error - // Code 0 // Data [[31 test one]] // Insert 32 // Error - // Code 0 // Data [[32 test one]] } @@ -292,12 +286,12 @@ func ExampleInsertRequest_spaceAndIndexNames() { defer conn.Close() req := tarantool.NewInsertRequest(spaceName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -315,32 +309,28 @@ func ExampleDeleteRequest() { ).Get() // Delete tuple with primary key { 35 }. - resp, err := conn.Do(tarantool.NewDeleteRequest(spaceNo). + data, err := conn.Do(tarantool.NewDeleteRequest(spaceNo). Index(indexNo). Key([]interface{}{uint(35)}), ).Get() fmt.Println("Delete 35") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Delete tuple with primary key { 36 }. - resp, err = conn.Do(tarantool.NewDeleteRequest("test"). + data, err = conn.Do(tarantool.NewDeleteRequest("test"). Index("primary"). Key([]interface{}{uint(36)}), ).Get() fmt.Println("Delete 36") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Output: // Delete 35 // Error - // Code 0 // Data [[35 test one]] // Delete 36 // Error - // Code 0 // Data [[36 test one]] } @@ -350,12 +340,12 @@ func ExampleDeleteRequest_spaceAndIndexNames() { req := tarantool.NewDeleteRequest(spaceName) req.Index(indexName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -371,50 +361,42 @@ func ExampleReplaceRequest() { // Replace a tuple with primary key 13. // Note, Tuple is defined within tests, and has EncdodeMsgpack and // DecodeMsgpack methods. - resp, err := conn.Do(tarantool.NewReplaceRequest(spaceNo). + data, err := conn.Do(tarantool.NewReplaceRequest(spaceNo). Tuple([]interface{}{uint(13), 1}), ).Get() fmt.Println("Replace 13") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - resp, err = conn.Do(tarantool.NewReplaceRequest("test"). + fmt.Println("Data", data) + data, err = conn.Do(tarantool.NewReplaceRequest("test"). Tuple([]interface{}{uint(13), 1}), ).Get() fmt.Println("Replace 13") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - resp, err = conn.Do(tarantool.NewReplaceRequest("test"). + fmt.Println("Data", data) + data, err = conn.Do(tarantool.NewReplaceRequest("test"). Tuple(&Tuple{Id: 13, Msg: "test", Name: "eleven"}), ).Get() fmt.Println("Replace 13") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - resp, err = conn.Do(tarantool.NewReplaceRequest("test"). + fmt.Println("Data", data) + data, err = conn.Do(tarantool.NewReplaceRequest("test"). Tuple(&Tuple{Id: 13, Msg: "test", Name: "twelve"}), ).Get() fmt.Println("Replace 13") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Output: // Replace 13 // Error - // Code 0 // Data [[13 1]] // Replace 13 // Error - // Code 0 // Data [[13 1]] // Replace 13 // Error - // Code 0 // Data [[13 test eleven]] // Replace 13 // Error - // Code 0 // Data [[13 test twelve]] } @@ -423,12 +405,12 @@ func ExampleReplaceRequest_spaceAndIndexNames() { defer conn.Close() req := tarantool.NewReplaceRequest(spaceName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -453,12 +435,12 @@ func ExampleUpdateRequest() { Splice(1, 1, 2, "!!"). Insert(7, "new"). Assign(7, "updated")) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("error in do update request is %v", err) return } - fmt.Printf("response is %#v\n", resp.Data) + fmt.Printf("response is %#v\n", data) // Output: // response is []interface {}{[]interface {}{0x457, "t!!t", 2, 0, 1, 1, 0, "updated"}} } @@ -469,12 +451,12 @@ func ExampleUpdateRequest_spaceAndIndexNames() { req := tarantool.NewUpdateRequest(spaceName) req.Index(indexName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -486,33 +468,33 @@ func ExampleUpsertRequest() { req = tarantool.NewUpsertRequest(617). Tuple([]interface{}{uint(1113), "first", "first"}). Operations(tarantool.NewOperations().Assign(1, "updated")) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("error in do select upsert is %v", err) return } - fmt.Printf("response is %#v\n", resp.Data) + fmt.Printf("response is %#v\n", data) req = tarantool.NewUpsertRequest("test"). Tuple([]interface{}{uint(1113), "second", "second"}). Operations(tarantool.NewOperations().Assign(2, "updated")) fut := conn.Do(req) - resp, err = fut.Get() + data, err = fut.Get() if err != nil { fmt.Printf("error in do async upsert request is %v", err) return } - fmt.Printf("response is %#v\n", resp.Data) + fmt.Printf("response is %#v\n", data) req = tarantool.NewSelectRequest(617). Limit(100). Key(tarantool.IntKey{1113}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { fmt.Printf("error in do select request is %v", err) return } - fmt.Printf("response is %#v\n", resp.Data) + fmt.Printf("response is %#v\n", data) // Output: // response is []interface {}{} // response is []interface {}{} @@ -524,12 +506,12 @@ func ExampleUpsertRequest_spaceAndIndexNames() { defer conn.Close() req := tarantool.NewUpsertRequest(spaceName) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -538,17 +520,15 @@ func ExampleCallRequest() { defer conn.Close() // Call a function 'simple_concat' with arguments. - resp, err := conn.Do(tarantool.NewCallRequest("simple_concat"). + data, err := conn.Do(tarantool.NewCallRequest("simple_concat"). Args([]interface{}{"1"}), ).Get() fmt.Println("Call simple_concat()") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Output: // Call simple_concat() // Error - // Code 0 // Data [11] } @@ -557,15 +537,13 @@ func ExampleEvalRequest() { defer conn.Close() // Run raw Lua code. - resp, err := conn.Do(tarantool.NewEvalRequest("return 1 + 2")).Get() + data, err := conn.Do(tarantool.NewEvalRequest("return 1 + 2")).Get() fmt.Println("Eval 'return 1 + 2'") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) // Output: // Eval 'return 1 + 2' // Error - // Code 0 // Data [3] } @@ -586,13 +564,14 @@ func ExampleExecuteRequest() { req := tarantool.NewExecuteRequest( "CREATE TABLE SQL_TEST (id INTEGER PRIMARY KEY, name STRING)") - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err := resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // There are 4 options to pass named parameters to an SQL query: // 1) The simple map; @@ -623,55 +602,60 @@ func ExampleExecuteRequest() { req = tarantool.NewExecuteRequest( "CREATE TABLE SQL_TEST (id INTEGER PRIMARY KEY, name STRING)") req = req.Args(sqlBind1) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // 2) req = req.Args(sqlBind2) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // 3) req = req.Args(sqlBind3) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // 4) req = req.Args(sqlBind4) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // The way to pass positional arguments to an SQL query. req = tarantool.NewExecuteRequest( "SELECT id FROM SQL_TEST WHERE id=? AND name=?"). Args([]interface{}{2, "test"}) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) // The way to pass SQL expression with using custom packing/unpacking for // a type. @@ -690,13 +674,14 @@ func ExampleExecuteRequest() { req = tarantool.NewExecuteRequest( "SELECT id FROM SQL_TEST WHERE id=? AND name=?"). Args([]interface{}{tarantool.KeyValueBind{"id", 1}, "test"}) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) - fmt.Println("MetaData", resp.MetaData) - fmt.Println("SQL Info", resp.SQLInfo) + data, err = resp.Decode() + fmt.Println("Error", err) + fmt.Println("Data", data) + fmt.Println("MetaData", resp.MetaData()) + fmt.Println("SQL Info", resp.SQLInfo()) } func getTestTxnDialer() tarantool.Dialer { @@ -716,7 +701,6 @@ func getTestTxnDialer() tarantool.Dialer { func ExampleCommitRequest() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -733,22 +717,22 @@ func ExampleCommitRequest() { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{uint(1001), "commit_hello", "commit_world"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -758,42 +742,41 @@ func ExampleCommitRequest() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{uint(1001)}) - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream before commit: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream before commit: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Commit transaction req = tarantool.NewCommitRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Commit: %s", err.Error()) return } - fmt.Printf("Commit transaction: response is %#v\n", resp.Code) + fmt.Printf("Commit transaction: response is %#v\n", data) // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after commit: response is %#v\n", resp.Data) + fmt.Printf("Select after commit: response is %#v\n", data) } func ExampleRollbackRequest() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -810,22 +793,22 @@ func ExampleRollbackRequest() { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{uint(2001), "rollback_hello", "rollback_world"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -835,42 +818,41 @@ func ExampleRollbackRequest() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{uint(2001)}) - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Rollback: %s", err.Error()) return } - fmt.Printf("Rollback transaction: response is %#v\n", resp.Code) + fmt.Printf("Rollback transaction: response is %#v\n", data) // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after Rollback: response is %#v\n", resp.Data) + fmt.Printf("Select after Rollback: response is %#v\n", data) } func ExampleBeginRequest_TxnIsolation() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -889,22 +871,22 @@ func ExampleBeginRequest_TxnIsolation() { req = tarantool.NewBeginRequest(). TxnIsolation(tarantool.ReadConfirmedLevel). Timeout(500 * time.Millisecond) - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{uint(2001), "rollback_hello", "rollback_world"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -914,37 +896,37 @@ func ExampleBeginRequest_TxnIsolation() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{uint(2001)}) - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Rollback: %s", err.Error()) return } - fmt.Printf("Rollback transaction: response is %#v\n", resp.Code) + fmt.Printf("Rollback transaction: response is %#v\n", data) // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after Rollback: response is %#v\n", resp.Data) + fmt.Printf("Select after Rollback: response is %#v\n", data) } func ExampleFuture_GetIterator() { @@ -959,14 +941,15 @@ func ExampleFuture_GetIterator() { var it tarantool.ResponseIterator for it = fut.GetIterator().WithTimeout(timeout); it.Next(); { resp := it.Value() - if resp.Code == tarantool.PushCode { + data, _ := resp.Decode() + if it.IsPush() { // It is a push message. - fmt.Printf("push message: %v\n", resp.Data[0]) - } else if resp.Code == tarantool.OkCode { + fmt.Printf("push message: %v\n", data[0]) + } else if resp.Header().Code == tarantool.OkCode { // It is a regular response. - fmt.Printf("response: %v", resp.Data[0]) + fmt.Printf("response: %v", data[0]) } else { - fmt.Printf("an unexpected response code %d", resp.Code) + fmt.Printf("an unexpected response code %d", resp.Header().Code) } } if err := it.Err(); err != nil { @@ -1148,11 +1131,11 @@ func ExampleConnection_Do() { // When the future receives the response, the result of the Future is set // and becomes available. We could wait for that moment with Future.Get() // or Future.GetTyped() methods. - resp, err := future.Get() + data, err := future.Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } // Output: @@ -1172,9 +1155,9 @@ func ExampleConnection_Do_failure() { future := conn.Do(req) // When the future receives the response, the result of the Future is set - // and becomes available. We could wait for that moment with Future.Get() - // or Future.GetTyped() methods. - resp, err := future.Get() + // and becomes available. We could wait for that moment with Future.Get(), + // Future.GetResponse() or Future.GetTyped() methods. + resp, err := future.GetResponse() if err != nil { // We don't print the error here to keep the example reproducible. // fmt.Printf("Failed to execute the request: %s\n", err) @@ -1184,8 +1167,8 @@ func ExampleConnection_Do_failure() { } else { // Response exist. So it could be a Tarantool error or a decode // error. We need to check the error code. - fmt.Printf("Error code from the response: %d\n", resp.Code) - if resp.Code == tarantool.OkCode { + fmt.Printf("Error code from the response: %d\n", resp.Header().Code) + if resp.Header().Code == tarantool.OkCode { fmt.Printf("Decode error: %s\n", err) } else { code := err.(tarantool.Error).Code @@ -1326,7 +1309,7 @@ func ExampleConnection_CloseGraceful_force() { // Force Connection.Close()! // Connection.CloseGraceful() done! // Result: - // connection closed by client (0x4001) + // [] connection closed by client (0x4001) } func ExampleWatchOnceRequest() { @@ -1344,11 +1327,11 @@ func ExampleWatchOnceRequest() { conn.Do(tarantool.NewBroadcastRequest(key).Value(value)).Get() - resp, err := conn.Do(tarantool.NewWatchOnceRequest(key)).Get() + data, err := conn.Do(tarantool.NewWatchOnceRequest(key)).Get() if err != nil { fmt.Printf("Failed to execute the request: %s\n", err) } else { - fmt.Println(resp.Data) + fmt.Println(data) } } @@ -1377,8 +1360,8 @@ func ExampleFdDialer() { fmt.Printf("connect error: %v\n", err) return } - resp, err := conn.Do(tarantool.NewPingRequest()).Get() - fmt.Println(resp.Code, err) + _, err = conn.Do(tarantool.NewPingRequest()).Get() + fmt.Println(err) // Output: - // 0 + // } diff --git a/future.go b/future.go index e7f7dae19..7281b6149 100644 --- a/future.go +++ b/future.go @@ -11,8 +11,8 @@ type Future struct { next *Future timeout time.Duration mutex sync.Mutex - pushes []*Response - resp *Response + pushes []Response + resp Response err error ready chan struct{} done chan struct{} @@ -40,7 +40,7 @@ func (fut *Future) isDone() bool { type asyncResponseIterator struct { fut *Future timeout time.Duration - resp *Response + resp Response err error curPos int done bool @@ -77,24 +77,24 @@ func (it *asyncResponseIterator) Next() bool { return false } - if it.err = it.resp.decodeBody(); it.err != nil { - it.resp = nil - return false - } - if last { it.done = true } else { + it.err = nil it.curPos += 1 } return true } -func (it *asyncResponseIterator) Value() *Response { +func (it *asyncResponseIterator) Value() Response { return it.resp } +func (it *asyncResponseIterator) IsPush() bool { + return !it.done +} + func (it *asyncResponseIterator) Err() error { return it.err } @@ -104,7 +104,7 @@ func (it *asyncResponseIterator) WithTimeout(timeout time.Duration) TimeoutRespo return it } -func (it *asyncResponseIterator) nextResponse() (resp *Response) { +func (it *asyncResponseIterator) nextResponse() (resp Response) { fut := it.fut pushesLen := len(fut.pushes) @@ -122,7 +122,7 @@ func NewFuture() (fut *Future) { fut = &Future{} fut.ready = make(chan struct{}, 1000000000) fut.done = make(chan struct{}) - fut.pushes = make([]*Response, 0) + fut.pushes = make([]Response, 0) return fut } @@ -131,21 +131,20 @@ func NewFuture() (fut *Future) { // // Deprecated: the method will be removed in the next major version, // use Connector.NewWatcher() instead of box.session.push(). -func (fut *Future) AppendPush(resp *Response) { +func (fut *Future) AppendPush(resp Response) { fut.mutex.Lock() defer fut.mutex.Unlock() if fut.isDone() { return } - resp.Code = PushCode fut.pushes = append(fut.pushes, resp) fut.ready <- struct{}{} } // SetResponse sets a response for the future and finishes the future. -func (fut *Future) SetResponse(resp *Response) { +func (fut *Future) SetResponse(resp Response) { fut.mutex.Lock() defer fut.mutex.Unlock() @@ -172,24 +171,35 @@ func (fut *Future) SetError(err error) { close(fut.done) } -// Get waits for Future to be filled and returns Response and error. -// -// Response will contain deserialized result in Data field. -// It will be []interface{}, so if you want more performance, use GetTyped method. +// GetResponse waits for Future to be filled and returns Response and error. // // Note: Response could be equal to nil if ClientError is returned in error. // // "error" could be Error, if it is error returned by Tarantool, // or ClientError, if something bad happens in a client process. -func (fut *Future) Get() (*Response, error) { +func (fut *Future) GetResponse() (Response, error) { fut.wait() if fut.err != nil { return fut.resp, fut.err } - err := fut.resp.decodeBody() + _, err := fut.resp.Decode() return fut.resp, err } +// Get waits for Future to be filled and returns the data of the Response and error. +// +// The data will be []interface{}, so if you want more performance, use GetTyped method. +// +// "error" could be Error, if it is error returned by Tarantool, +// or ClientError, if something bad happens in a client process. +func (fut *Future) Get() ([]interface{}, error) { + fut.wait() + if fut.err != nil { + return nil, fut.err + } + return fut.resp.Decode() +} + // GetTyped waits for Future and calls msgpack.Decoder.Decode(result) if no error happens. // It is could be much faster than Get() function. // @@ -199,8 +209,7 @@ func (fut *Future) GetTyped(result interface{}) error { if fut.err != nil { return fut.err } - err := fut.resp.decodeBodyTyped(result) - return err + return fut.resp.DecodeTyped(result) } // GetIterator returns an iterator for iterating through push messages diff --git a/future_test.go b/future_test.go index 274bee7d5..3a3d8d01d 100644 --- a/future_test.go +++ b/future_test.go @@ -6,11 +6,12 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" . "github.com/tarantool/go-tarantool/v2" ) func assertResponseIteratorValue(t testing.TB, it ResponseIterator, - code uint32, resp *Response) { + isPush bool, resp Response) { t.Helper() if it.Err() != nil { @@ -19,13 +20,15 @@ func assertResponseIteratorValue(t testing.TB, it ResponseIterator, if it.Value() == nil { t.Errorf("An unexpected nil value") - } else if it.Value().Code != code { - t.Errorf("An unexpected response code %d, expected %d", it.Value().Code, code) + } else if it.IsPush() != isPush { + if isPush { + t.Errorf("An unexpected response type, expected to be push") + } else { + t.Errorf("An unexpected response type, expected not to be push") + } } - if it.Value() != resp { - t.Errorf("An unexpected response %v, expected %v", it.Value(), resp) - } + assert.Equalf(t, it.Value(), resp, "An unexpected response %v, expected %v", it.Value(), resp) } func assertResponseIteratorFinished(t testing.TB, it ResponseIterator) { @@ -51,12 +54,12 @@ func TestFutureGetIteratorNoItems(t *testing.T) { } func TestFutureGetIteratorNoResponse(t *testing.T) { - push := &Response{} + push := &ConnResponse{} fut := NewFuture() fut.AppendPush(push) if it := fut.GetIterator(); it.Next() { - assertResponseIteratorValue(t, it, PushCode, push) + assertResponseIteratorValue(t, it, true, push) if it.Next() == true { t.Errorf("An unexpected next value.") } @@ -67,12 +70,12 @@ func TestFutureGetIteratorNoResponse(t *testing.T) { } func TestFutureGetIteratorNoResponseTimeout(t *testing.T) { - push := &Response{} + push := &ConnResponse{} fut := NewFuture() fut.AppendPush(push) if it := fut.GetIterator().WithTimeout(1 * time.Nanosecond); it.Next() { - assertResponseIteratorValue(t, it, PushCode, push) + assertResponseIteratorValue(t, it, true, push) if it.Next() == true { t.Errorf("An unexpected next value.") } @@ -83,8 +86,8 @@ func TestFutureGetIteratorNoResponseTimeout(t *testing.T) { } func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { - push := &Response{} - resp := &Response{} + push := &ConnResponse{} + resp := &ConnResponse{} fut := NewFuture() fut.AppendPush(push) @@ -99,13 +102,14 @@ func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { var it ResponseIterator var cnt = 0 for it = fut.GetIterator().WithTimeout(5 * time.Second); it.Next(); { - code := PushCode - r := push + var r Response + isPush := true + r = push if cnt == 1 { - code = OkCode + isPush = false r = resp } - assertResponseIteratorValue(t, it, code, r) + assertResponseIteratorValue(t, it, isPush, r) cnt += 1 if cnt == 1 { wait.Done() @@ -124,14 +128,14 @@ func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { } func TestFutureGetIteratorFirstResponse(t *testing.T) { - resp1 := &Response{} - resp2 := &Response{} + resp1 := &ConnResponse{} + resp2 := &ConnResponse{} fut := NewFuture() fut.SetResponse(resp1) fut.SetResponse(resp2) if it := fut.GetIterator(); it.Next() { - assertResponseIteratorValue(t, it, OkCode, resp1) + assertResponseIteratorValue(t, it, false, resp1) if it.Next() == true { t.Errorf("An unexpected next value.") } @@ -160,10 +164,10 @@ func TestFutureGetIteratorFirstError(t *testing.T) { } func TestFutureGetIteratorResponse(t *testing.T) { - responses := []*Response{ + responses := []*ConnResponse{ + {}, {}, {}, - {Code: OkCode}, } fut := NewFuture() for i, resp := range responses { @@ -181,11 +185,11 @@ func TestFutureGetIteratorResponse(t *testing.T) { for _, it := range its { var cnt = 0 for it.Next() { - code := PushCode + isPush := true if cnt == len(responses)-1 { - code = OkCode + isPush = false } - assertResponseIteratorValue(t, it, code, responses[cnt]) + assertResponseIteratorValue(t, it, isPush, responses[cnt]) cnt += 1 } assertResponseIteratorFinished(t, it) @@ -198,7 +202,7 @@ func TestFutureGetIteratorResponse(t *testing.T) { func TestFutureGetIteratorError(t *testing.T) { const errMsg = "error message" - responses := []*Response{ + responses := []*ConnResponse{ {}, {}, } @@ -216,8 +220,7 @@ func TestFutureGetIteratorError(t *testing.T) { for _, it := range its { var cnt = 0 for it.Next() { - code := PushCode - assertResponseIteratorValue(t, it, code, responses[cnt]) + assertResponseIteratorValue(t, it, true, responses[cnt]) cnt += 1 } if err = it.Err(); err != nil { @@ -236,14 +239,14 @@ func TestFutureGetIteratorError(t *testing.T) { func TestFutureSetStateRaceCondition(t *testing.T) { err := errors.New("any error") - resp := &Response{} + resp := &ConnResponse{} for i := 0; i < 1000; i++ { fut := NewFuture() for j := 0; j < 9; j++ { go func(opt int) { if opt%3 == 0 { - respAppend := &Response{} + respAppend := &ConnResponse{} fut.AppendPush(respAppend) } else if opt%3 == 1 { fut.SetError(err) diff --git a/header.go b/header.go new file mode 100644 index 000000000..d9069c23a --- /dev/null +++ b/header.go @@ -0,0 +1,10 @@ +package tarantool + +// Header is a response header. +type Header struct { + // RequestId is an id of a corresponding request. + RequestId uint32 + // Code is a response code. It could be used to check that response + // has or hasn't an error. + Code uint32 +} diff --git a/pool/connection_pool.go b/pool/connection_pool.go index c272310bd..747de45f4 100644 --- a/pool/connection_pool.go +++ b/pool/connection_pool.go @@ -373,7 +373,7 @@ func (p *ConnectionPool) GetInfo() map[string]ConnectionInfo { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (p *ConnectionPool) Ping(userMode Mode) (*tarantool.Response, error) { +func (p *ConnectionPool) Ping(userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -388,7 +388,7 @@ func (p *ConnectionPool) Ping(userMode Mode) (*tarantool.Response, error) { // use a SelectRequest object + Do() instead. func (p *ConnectionPool) Select(space, index interface{}, offset, limit uint32, - iterator tarantool.Iter, key interface{}, userMode ...Mode) (*tarantool.Response, error) { + iterator tarantool.Iter, key interface{}, userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(ANY, userMode) if err != nil { return nil, err @@ -403,7 +403,7 @@ func (p *ConnectionPool) Select(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. func (p *ConnectionPool) Insert(space interface{}, tuple interface{}, - userMode ...Mode) (*tarantool.Response, error) { + userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -418,7 +418,7 @@ func (p *ConnectionPool) Insert(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. func (p *ConnectionPool) Replace(space interface{}, tuple interface{}, - userMode ...Mode) (*tarantool.Response, error) { + userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -433,7 +433,7 @@ func (p *ConnectionPool) Replace(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. func (p *ConnectionPool) Delete(space, index interface{}, key interface{}, - userMode ...Mode) (*tarantool.Response, error) { + userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -448,7 +448,7 @@ func (p *ConnectionPool) Delete(space, index interface{}, key interface{}, // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. func (p *ConnectionPool) Update(space, index interface{}, key interface{}, - ops *tarantool.Operations, userMode ...Mode) (*tarantool.Response, error) { + ops *tarantool.Operations, userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -463,7 +463,7 @@ func (p *ConnectionPool) Update(space, index interface{}, key interface{}, // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. func (p *ConnectionPool) Upsert(space interface{}, tuple interface{}, - ops *tarantool.Operations, userMode ...Mode) (*tarantool.Response, error) { + ops *tarantool.Operations, userMode ...Mode) (tarantool.Response, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -478,7 +478,7 @@ func (p *ConnectionPool) Upsert(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. func (p *ConnectionPool) Call(functionName string, args interface{}, - userMode Mode) (*tarantool.Response, error) { + userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -494,7 +494,7 @@ func (p *ConnectionPool) Call(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. func (p *ConnectionPool) Call16(functionName string, args interface{}, - userMode Mode) (*tarantool.Response, error) { + userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -509,7 +509,7 @@ func (p *ConnectionPool) Call16(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. func (p *ConnectionPool) Call17(functionName string, args interface{}, - userMode Mode) (*tarantool.Response, error) { + userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -523,7 +523,7 @@ func (p *ConnectionPool) Call17(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. func (p *ConnectionPool) Eval(expr string, args interface{}, - userMode Mode) (*tarantool.Response, error) { + userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -537,7 +537,7 @@ func (p *ConnectionPool) Eval(expr string, args interface{}, // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. func (p *ConnectionPool) Execute(expr string, args interface{}, - userMode Mode) (*tarantool.Response, error) { + userMode Mode) (tarantool.Response, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -980,18 +980,15 @@ func (p *ConnectionPool) Do(req tarantool.Request, userMode Mode) *tarantool.Fut // func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, error) { - resp, err := conn.Do(tarantool.NewCallRequest("box.info")).Get() + data, err := conn.Do(tarantool.NewCallRequest("box.info")).Get() if err != nil { return UnknownRole, err } - if resp == nil { - return UnknownRole, ErrIncorrectResponse - } - if len(resp.Data) < 1 { + if len(data) < 1 { return UnknownRole, ErrIncorrectResponse } - instanceStatus, ok := resp.Data[0].(map[interface{}]interface{})["status"] + instanceStatus, ok := data[0].(map[interface{}]interface{})["status"] if !ok { return UnknownRole, ErrIncorrectResponse } @@ -999,7 +996,7 @@ func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, er return UnknownRole, ErrIncorrectStatus } - replicaRole, ok := resp.Data[0].(map[interface{}]interface{})["ro"] + replicaRole, ok := data[0].(map[interface{}]interface{})["ro"] if !ok { return UnknownRole, ErrIncorrectResponse } diff --git a/pool/connection_pool_test.go b/pool/connection_pool_test.go index 15e67b59d..50096cc4c 100644 --- a/pool/connection_pool_test.go +++ b/pool/connection_pool_test.go @@ -814,9 +814,8 @@ func TestCloseGraceful(t *testing.T) { require.ErrorContains(t, err, "can't find healthy instance in pool") // Check that a previous request was successful. - resp, err := fut.Get() + _, err = fut.Get() require.Nilf(t, err, "sleep request no error") - require.NotNilf(t, resp, "sleep response exists") args = test_helpers.CheckStatusesArgs{ ConnPool: connPool, @@ -1144,9 +1143,11 @@ func TestCall(t *testing.T) { resp, err := connPool.Call("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val := resp.Data[0].(map[interface{}]interface{})["ro"] + val := data[0].(map[interface{}]interface{})["ro"] ro, ok := val.(bool) require.Truef(t, ok, "expected `true` with mode `PreferRO`") require.Truef(t, ro, "expected `true` with mode `PreferRO`") @@ -1155,9 +1156,11 @@ func TestCall(t *testing.T) { resp, err = connPool.Call("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `PreferRW`") require.Falsef(t, ro, "expected `false` with mode `PreferRW`") @@ -1166,9 +1169,11 @@ func TestCall(t *testing.T) { resp, err = connPool.Call("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `true` with mode `RO`") require.Truef(t, ro, "expected `true` with mode `RO`") @@ -1177,9 +1182,11 @@ func TestCall(t *testing.T) { resp, err = connPool.Call("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `RW`") require.Falsef(t, ro, "expected `false` with mode `RW`") @@ -1203,9 +1210,11 @@ func TestCall16(t *testing.T) { resp, err := connPool.Call16("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val := resp.Data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] + val := data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] ro, ok := val.(bool) require.Truef(t, ok, "expected `true` with mode `PreferRO`") require.Truef(t, ro, "expected `true` with mode `PreferRO`") @@ -1214,9 +1223,11 @@ func TestCall16(t *testing.T) { resp, err = connPool.Call16("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] + val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `PreferRW`") require.Falsef(t, ro, "expected `false` with mode `PreferRW`") @@ -1225,9 +1236,11 @@ func TestCall16(t *testing.T) { resp, err = connPool.Call16("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] + val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `true` with mode `RO`") require.Truef(t, ro, "expected `true` with mode `RO`") @@ -1236,9 +1249,11 @@ func TestCall16(t *testing.T) { resp, err = connPool.Call16("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] + val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `RW`") require.Falsef(t, ro, "expected `false` with mode `RW`") @@ -1262,9 +1277,11 @@ func TestCall17(t *testing.T) { resp, err := connPool.Call17("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val := resp.Data[0].(map[interface{}]interface{})["ro"] + val := data[0].(map[interface{}]interface{})["ro"] ro, ok := val.(bool) require.Truef(t, ok, "expected `true` with mode `PreferRO`") require.Truef(t, ro, "expected `true` with mode `PreferRO`") @@ -1273,9 +1290,11 @@ func TestCall17(t *testing.T) { resp, err = connPool.Call17("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `PreferRW`") require.Falsef(t, ro, "expected `false` with mode `PreferRW`") @@ -1284,9 +1303,11 @@ func TestCall17(t *testing.T) { resp, err = connPool.Call17("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `true` with mode `RO`") require.Truef(t, ro, "expected `true` with mode `RO`") @@ -1295,9 +1316,11 @@ func TestCall17(t *testing.T) { resp, err = connPool.Call17("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") require.NotNilf(t, resp, "response is nil after Call") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Call") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") - val = resp.Data[0].(map[interface{}]interface{})["ro"] + val = data[0].(map[interface{}]interface{})["ro"] ro, ok = val.(bool) require.Truef(t, ok, "expected `false` with mode `RW`") require.Falsef(t, ro, "expected `false` with mode `RW`") @@ -1321,9 +1344,11 @@ func TestEval(t *testing.T) { resp, err := connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Eval") require.NotNilf(t, resp, "response is nil after Eval") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Eval") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") - val, ok := resp.Data[0].(bool) + val, ok := data[0].(bool) require.Truef(t, ok, "expected `true` with mode `PreferRO`") require.Truef(t, val, "expected `true` with mode `PreferRO`") @@ -1331,9 +1356,11 @@ func TestEval(t *testing.T) { resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Eval") require.NotNilf(t, resp, "response is nil after Eval") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Eval") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") - val, ok = resp.Data[0].(bool) + val, ok = data[0].(bool) require.Truef(t, ok, "expected `false` with mode `PreferRW`") require.Falsef(t, val, "expected `false` with mode `PreferRW`") @@ -1341,9 +1368,11 @@ func TestEval(t *testing.T) { resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Eval") require.NotNilf(t, resp, "response is nil after Eval") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Eval") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") - val, ok = resp.Data[0].(bool) + val, ok = data[0].(bool) require.Truef(t, ok, "expected `true` with mode `RO`") require.Truef(t, val, "expected `true` with mode `RO`") @@ -1351,9 +1380,11 @@ func TestEval(t *testing.T) { resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Eval") require.NotNilf(t, resp, "response is nil after Eval") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Eval") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") - val, ok = resp.Data[0].(bool) + val, ok = data[0].(bool) require.Truef(t, ok, "expected `false` with mode `RW`") require.Falsef(t, val, "expected `false` with mode `RW`") } @@ -1402,8 +1433,10 @@ func TestExecute(t *testing.T) { resp, err := connPool.Execute(request, []interface{}{}, pool.ANY) require.Nilf(t, err, "failed to Execute") require.NotNilf(t, resp, "response is nil after Execute") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after Execute") - require.Equalf(t, len(resp.Data[0].([]interface{})), 2, "unexpected response") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Execute") + require.Equalf(t, len(data[0].([]interface{})), 2, "unexpected response") // ExecuteTyped mem := []Member{} @@ -1414,11 +1447,10 @@ func TestExecute(t *testing.T) { // ExecuteAsync fut := connPool.ExecuteAsync(request, []interface{}{}, pool.ANY) - resp, err = fut.Get() + data, err = fut.Get() require.Nilf(t, err, "failed to ExecuteAsync") - require.NotNilf(t, resp, "response is nil after ExecuteAsync") - require.GreaterOrEqualf(t, len(resp.Data), 1, "response.Data is empty after ExecuteAsync") - require.Equalf(t, len(resp.Data[0].([]interface{})), 2, "unexpected response") + require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after ExecuteAsync") + require.Equalf(t, len(data[0].([]interface{})), 2, "unexpected response") } func TestRoundRobinStrategy(t *testing.T) { @@ -1843,9 +1875,11 @@ func TestInsert(t *testing.T) { resp, err := connPool.Insert(spaceName, []interface{}{"rw_insert_key", "rw_insert_value"}) require.Nilf(t, err, "failed to Insert") require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Insert") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Insert") require.Equalf(t, 2, len(tpl), "unexpected body of Insert") @@ -1867,12 +1901,11 @@ func TestInsert(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"rw_insert_key"}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -1889,9 +1922,11 @@ func TestInsert(t *testing.T) { []interface{}{"preferRW_insert_key", "preferRW_insert_value"}) require.Nilf(t, err, "failed to Insert") require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Insert") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Insert") require.Equalf(t, 2, len(tpl), "unexpected body of Insert") @@ -1908,12 +1943,11 @@ func TestInsert(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"preferRW_insert_key"}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -1946,12 +1980,11 @@ func TestDelete(t *testing.T) { defer conn.Close() ins := tarantool.NewInsertRequest(spaceNo).Tuple([]interface{}{"delete_key", "delete_value"}) - resp, err := conn.Do(ins).Get() + data, err := conn.Do(ins).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Insert") + require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Insert") require.Equalf(t, 2, len(tpl), "unexpected body of Insert") @@ -1964,12 +1997,14 @@ func TestDelete(t *testing.T) { require.Equalf(t, "delete_value", value, "unexpected body of Insert (1)") // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err = connPool.Delete(spaceName, indexNo, []interface{}{"delete_key"}) + resp, err := connPool.Delete(spaceName, indexNo, []interface{}{"delete_key"}) require.Nilf(t, err, "failed to Delete") require.NotNilf(t, resp, "response is nil after Delete") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Delete") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Delete") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Delete") require.Equalf(t, 2, len(tpl), "unexpected body of Delete") @@ -1986,10 +2021,9 @@ func TestDelete(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"delete_key"}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Body len != 0 after Select") + require.Equalf(t, 0, len(data), "response Body len != 0 after Select") } func TestUpsert(t *testing.T) { @@ -2023,12 +2057,11 @@ func TestUpsert(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"upsert_key"}) - resp, err = conn.Do(sel).Get() + data, err := conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2048,12 +2081,11 @@ func TestUpsert(t *testing.T) { require.Nilf(t, err, "failed to Upsert") require.NotNilf(t, resp, "response is nil after Upsert") - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2087,12 +2119,11 @@ func TestUpdate(t *testing.T) { ins := tarantool.NewInsertRequest(spaceNo). Tuple([]interface{}{"update_key", "update_value"}) - resp, err := conn.Do(ins).Get() + data, err := conn.Do(ins).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Insert") + require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Insert") require.Equalf(t, 2, len(tpl), "unexpected body of Insert") @@ -2105,7 +2136,7 @@ func TestUpdate(t *testing.T) { require.Equalf(t, "update_value", value, "unexpected body of Insert (1)") // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err = connPool.Update(spaceName, indexNo, + resp, err := connPool.Update(spaceName, indexNo, []interface{}{"update_key"}, tarantool.NewOperations().Assign(1, "new_value")) require.Nilf(t, err, "failed to Update") require.NotNilf(t, resp, "response is nil after Update") @@ -2115,12 +2146,11 @@ func TestUpdate(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"update_key"}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2140,12 +2170,11 @@ func TestUpdate(t *testing.T) { require.Nilf(t, err, "failed to Update") require.NotNilf(t, resp, "response is nil after Update") - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2179,12 +2208,11 @@ func TestReplace(t *testing.T) { ins := tarantool.NewInsertRequest(spaceNo). Tuple([]interface{}{"replace_key", "replace_value"}) - resp, err := conn.Do(ins).Get() + data, err := conn.Do(ins).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Insert") + require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Insert") require.Equalf(t, 2, len(tpl), "unexpected body of Insert") @@ -2197,7 +2225,7 @@ func TestReplace(t *testing.T) { require.Equalf(t, "replace_value", value, "unexpected body of Insert (1)") // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err = connPool.Replace(spaceNo, []interface{}{"new_key", "new_value"}) + resp, err := connPool.Replace(spaceNo, []interface{}{"new_key", "new_value"}) require.Nilf(t, err, "failed to Replace") require.NotNilf(t, resp, "response is nil after Replace") @@ -2206,12 +2234,11 @@ func TestReplace(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"new_key"}) - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2228,12 +2255,11 @@ func TestReplace(t *testing.T) { require.Nilf(t, err, "failed to Replace") require.NotNilf(t, resp, "response is nil after Replace") - resp, err = conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2285,9 +2311,11 @@ func TestSelect(t *testing.T) { resp, err := connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, anyKey) require.Nilf(t, err, "failed to Select") require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + data, err := resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2303,9 +2331,11 @@ func TestSelect(t *testing.T) { resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.PreferRO) require.Nilf(t, err, "failed to Select") require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2317,9 +2347,11 @@ func TestSelect(t *testing.T) { resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.PreferRW) require.Nilf(t, err, "failed to Select") require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2335,9 +2367,11 @@ func TestSelect(t *testing.T) { resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.RO) require.Nilf(t, err, "failed to Select") require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2353,9 +2387,11 @@ func TestSelect(t *testing.T) { resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.RW) require.Nilf(t, err, "failed to Select") require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + data, err = resp.Decode() + require.Nilf(t, err, "failed to Decode") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2424,29 +2460,24 @@ func TestDo(t *testing.T) { req := tarantool.NewPingRequest() // ANY - resp, err := connPool.Do(req, pool.ANY).Get() + _, err = connPool.Do(req, pool.ANY).Get() require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") // RW - resp, err = connPool.Do(req, pool.RW).Get() + _, err = connPool.Do(req, pool.RW).Get() require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") // RO - resp, err = connPool.Do(req, pool.RO).Get() + _, err = connPool.Do(req, pool.RO).Get() require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") // PreferRW - resp, err = connPool.Do(req, pool.PreferRW).Get() + _, err = connPool.Do(req, pool.PreferRW).Get() require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") // PreferRO - resp, err = connPool.Do(req, pool.PreferRO).Get() + _, err = connPool.Do(req, pool.PreferRO).Get() require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") } func TestDo_concurrent(t *testing.T) { @@ -2503,34 +2534,33 @@ func TestNewPrepared(t *testing.T) { executeReq := tarantool.NewExecutePreparedRequest(stmt) unprepareReq := tarantool.NewUnprepareRequest(stmt) - resp, err := connPool.Do(executeReq.Args([]interface{}{1, "test"}), pool.ANY).Get() + resp, err := connPool.Do(executeReq.Args([]interface{}{1, "test"}), pool.ANY).GetResponse() if err != nil { t.Fatalf("failed to execute prepared: %v", err) } if resp == nil { t.Fatalf("nil response") } - if resp.Code != tarantool.OkCode { - t.Fatalf("failed to execute prepared: code %d", resp.Code) + data, err := resp.Decode() + if err != nil { + t.Fatalf("failed to Decode: %s", err.Error()) } - if reflect.DeepEqual(resp.Data[0], []interface{}{1, "test"}) { + if reflect.DeepEqual(data[0], []interface{}{1, "test"}) { t.Error("Select with named arguments failed") } - if resp.MetaData[0].FieldType != "unsigned" || - resp.MetaData[0].FieldName != "NAME0" || - resp.MetaData[1].FieldType != "string" || - resp.MetaData[1].FieldName != "NAME1" { + metaData := resp.MetaData() + if metaData[0].FieldType != "unsigned" || + metaData[0].FieldName != "NAME0" || + metaData[1].FieldType != "string" || + metaData[1].FieldName != "NAME1" { t.Error("Wrong metadata") } // the second argument for unprepare request is unused - it already belongs to some connection - resp, err = connPool.Do(unprepareReq, pool.ANY).Get() + _, err = connPool.Do(unprepareReq, pool.ANY).Get() if err != nil { t.Errorf("failed to unprepare prepared statement: %v", err) } - if resp.Code != tarantool.OkCode { - t.Errorf("failed to unprepare prepared statement: code %d", resp.Code) - } _, err = connPool.Do(unprepareReq, pool.ANY).Get() if err == nil { @@ -2575,7 +2605,6 @@ func TestDoWithStrangerConn(t *testing.T) { func TestStream_Commit(t *testing.T) { var req tarantool.Request - var resp *tarantool.Response var err error test_helpers.SkipIfStreamsUnsupported(t) @@ -2598,18 +2627,14 @@ func TestStream_Commit(t *testing.T) { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Begin") - require.NotNilf(t, resp, "response is nil after Begin") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Begin: wrong code returned") // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"commit_key", "commit_value"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Insert: wrong code returned") // Connect to servers[2] to check if tuple // was inserted outside of stream on RW instance @@ -2625,18 +2650,16 @@ func TestStream_Commit(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"commit_key"}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 1, len(resp.Data), "response Body len != 1 after Select") + require.Equalf(t, 1, len(data), "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2650,18 +2673,15 @@ func TestStream_Commit(t *testing.T) { // Commit transaction req = tarantool.NewCommitRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Commit") - require.NotNilf(t, resp, "response is nil after Commit") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Commit: wrong code returned") // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, len(resp.Data), 1, "response Body len != 1 after Select") + require.Equalf(t, len(data), 1, "response Body len != 1 after Select") - tpl, ok = resp.Data[0].([]interface{}) + tpl, ok = data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2676,7 +2696,6 @@ func TestStream_Commit(t *testing.T) { func TestStream_Rollback(t *testing.T) { var req tarantool.Request - var resp *tarantool.Response var err error test_helpers.SkipIfStreamsUnsupported(t) @@ -2699,18 +2718,14 @@ func TestStream_Rollback(t *testing.T) { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Begin") - require.NotNilf(t, resp, "response is nil after Begin") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Begin: wrong code returned") // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"rollback_key", "rollback_value"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Insert: wrong code returned") // Connect to servers[2] to check if tuple // was not inserted outside of stream on RW instance @@ -2725,18 +2740,16 @@ func TestStream_Rollback(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"rollback_key"}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 1, len(resp.Data), "response Body len != 1 after Select") + require.Equalf(t, 1, len(data), "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2750,27 +2763,22 @@ func TestStream_Rollback(t *testing.T) { // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Rollback") - require.NotNilf(t, resp, "response is nil after Rollback") - require.Equalf(t, tarantool.OkCode, resp.Code, "failed to Rollback: wrong code returned") // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Body len != 0 after Select") + require.Equalf(t, 0, len(data), "response Body len != 0 after Select") // Select inside of stream after rollback - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Body len != 0 after Select") + require.Equalf(t, 0, len(data), "response Body len != 0 after Select") } func TestStream_TxnIsolationLevel(t *testing.T) { var req tarantool.Request - var resp *tarantool.Response var err error txnIsolationLevels := []tarantool.TxnIsolationLevel{ @@ -2806,18 +2814,14 @@ func TestStream_TxnIsolationLevel(t *testing.T) { for _, level := range txnIsolationLevels { // Begin transaction req = tarantool.NewBeginRequest().TxnIsolation(level).Timeout(500 * time.Millisecond) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Begin") - require.NotNilf(t, resp, "response is nil after Begin") - require.Equalf(t, tarantool.OkCode, resp.Code, "wrong code returned") // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"level_key", "level_value"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, tarantool.OkCode, resp.Code, "wrong code returned") // Select not related to the transaction // while transaction is not committed @@ -2827,18 +2831,16 @@ func TestStream_TxnIsolationLevel(t *testing.T) { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"level_key"}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 1, len(resp.Data), "response Body len != 1 after Select") + require.Equalf(t, 1, len(data), "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 2, len(tpl), "unexpected body of Select") @@ -2852,22 +2854,18 @@ func TestStream_TxnIsolationLevel(t *testing.T) { // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Rollback") - require.NotNilf(t, resp, "response is nil after Rollback") - require.Equalf(t, tarantool.OkCode, resp.Code, "wrong code returned") // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select inside of stream after rollback - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{"level_key"}) } diff --git a/pool/connector.go b/pool/connector.go index 604e3921f..74a60bd74 100644 --- a/pool/connector.go +++ b/pool/connector.go @@ -60,7 +60,7 @@ func (c *ConnectorAdapter) ConfiguredTimeout() time.Duration { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (c *ConnectorAdapter) Ping() (*tarantool.Response, error) { +func (c *ConnectorAdapter) Ping() (tarantool.Response, error) { return c.pool.Ping(c.mode) } @@ -70,7 +70,7 @@ func (c *ConnectorAdapter) Ping() (*tarantool.Response, error) { // use a SelectRequest object + Do() instead. func (c *ConnectorAdapter) Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, - key interface{}) (*tarantool.Response, error) { + key interface{}) (tarantool.Response, error) { return c.pool.Select(space, index, offset, limit, iterator, key, c.mode) } @@ -79,7 +79,7 @@ func (c *ConnectorAdapter) Select(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. func (c *ConnectorAdapter) Insert(space interface{}, - tuple interface{}) (*tarantool.Response, error) { + tuple interface{}) (tarantool.Response, error) { return c.pool.Insert(space, tuple, c.mode) } @@ -88,7 +88,7 @@ func (c *ConnectorAdapter) Insert(space interface{}, // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. func (c *ConnectorAdapter) Replace(space interface{}, - tuple interface{}) (*tarantool.Response, error) { + tuple interface{}) (tarantool.Response, error) { return c.pool.Replace(space, tuple, c.mode) } @@ -97,7 +97,7 @@ func (c *ConnectorAdapter) Replace(space interface{}, // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. func (c *ConnectorAdapter) Delete(space, index interface{}, - key interface{}) (*tarantool.Response, error) { + key interface{}) (tarantool.Response, error) { return c.pool.Delete(space, index, key, c.mode) } @@ -106,7 +106,7 @@ func (c *ConnectorAdapter) Delete(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. func (c *ConnectorAdapter) Update(space, index interface{}, - key interface{}, ops *tarantool.Operations) (*tarantool.Response, error) { + key interface{}, ops *tarantool.Operations) (tarantool.Response, error) { return c.pool.Update(space, index, key, ops, c.mode) } @@ -115,7 +115,7 @@ func (c *ConnectorAdapter) Update(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. func (c *ConnectorAdapter) Upsert(space, tuple interface{}, - ops *tarantool.Operations) (*tarantool.Response, error) { + ops *tarantool.Operations) (tarantool.Response, error) { return c.pool.Upsert(space, tuple, ops, c.mode) } @@ -125,7 +125,7 @@ func (c *ConnectorAdapter) Upsert(space, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. func (c *ConnectorAdapter) Call(functionName string, - args interface{}) (*tarantool.Response, error) { + args interface{}) (tarantool.Response, error) { return c.pool.Call(functionName, args, c.mode) } @@ -136,7 +136,7 @@ func (c *ConnectorAdapter) Call(functionName string, // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. func (c *ConnectorAdapter) Call16(functionName string, - args interface{}) (*tarantool.Response, error) { + args interface{}) (tarantool.Response, error) { return c.pool.Call16(functionName, args, c.mode) } @@ -146,7 +146,7 @@ func (c *ConnectorAdapter) Call16(functionName string, // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. func (c *ConnectorAdapter) Call17(functionName string, - args interface{}) (*tarantool.Response, error) { + args interface{}) (tarantool.Response, error) { return c.pool.Call17(functionName, args, c.mode) } @@ -155,7 +155,7 @@ func (c *ConnectorAdapter) Call17(functionName string, // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. func (c *ConnectorAdapter) Eval(expr string, - args interface{}) (*tarantool.Response, error) { + args interface{}) (tarantool.Response, error) { return c.pool.Eval(expr, args, c.mode) } @@ -164,7 +164,7 @@ func (c *ConnectorAdapter) Eval(expr string, // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. func (c *ConnectorAdapter) Execute(expr string, - args interface{}) (*tarantool.Response, error) { + args interface{}) (tarantool.Response, error) { return c.pool.Execute(expr, args, c.mode) } diff --git a/pool/connector_test.go b/pool/connector_test.go index fa107cc58..190d2f9cc 100644 --- a/pool/connector_test.go +++ b/pool/connector_test.go @@ -135,7 +135,7 @@ type baseRequestMock struct { mode Mode } -var reqResp *tarantool.Response = &tarantool.Response{} +var reqResp tarantool.Response = &tarantool.ConnResponse{} var errReq error = errors.New("response error") var reqFuture *tarantool.Future = &tarantool.Future{} @@ -190,7 +190,7 @@ type selectMock struct { func (m *selectMock) Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, key interface{}, - mode ...Mode) (*tarantool.Response, error) { + mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.index = index @@ -299,7 +299,7 @@ type insertMock struct { } func (m *insertMock) Insert(space, tuple interface{}, - mode ...Mode) (*tarantool.Response, error) { + mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.tuple = tuple @@ -380,7 +380,7 @@ type replaceMock struct { } func (m *replaceMock) Replace(space, tuple interface{}, - mode ...Mode) (*tarantool.Response, error) { + mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.tuple = tuple @@ -461,7 +461,7 @@ type deleteMock struct { } func (m *deleteMock) Delete(space, index, key interface{}, - mode ...Mode) (*tarantool.Response, error) { + mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.index = index @@ -548,7 +548,7 @@ type updateMock struct { } func (m *updateMock) Update(space, index, key interface{}, - ops *tarantool.Operations, mode ...Mode) (*tarantool.Response, error) { + ops *tarantool.Operations, mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.index = index @@ -641,7 +641,7 @@ type upsertMock struct { } func (m *upsertMock) Upsert(space, tuple interface{}, ops *tarantool.Operations, - mode ...Mode) (*tarantool.Response, error) { + mode ...Mode) (tarantool.Response, error) { m.called++ m.space = space m.tuple = tuple @@ -698,7 +698,7 @@ type baseCallMock struct { } func (m *baseCallMock) call(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { m.called++ m.functionName = functionName m.args = args @@ -730,7 +730,7 @@ type callMock struct { } func (m *callMock) Call(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { return m.call(functionName, args, mode) } @@ -801,7 +801,7 @@ type call16Mock struct { } func (m *call16Mock) Call16(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { return m.call(functionName, args, mode) } @@ -872,7 +872,7 @@ type call17Mock struct { } func (m *call17Mock) Call17(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { return m.call(functionName, args, mode) } @@ -943,7 +943,7 @@ type evalMock struct { } func (m *evalMock) Eval(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { return m.call(functionName, args, mode) } @@ -1014,7 +1014,7 @@ type executeMock struct { } func (m *executeMock) Execute(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) { + mode Mode) (tarantool.Response, error) { return m.call(functionName, args, mode) } diff --git a/pool/example_test.go b/pool/example_test.go index c4a919a93..7eb480177 100644 --- a/pool/example_test.go +++ b/pool/example_test.go @@ -157,7 +157,6 @@ func getTestTxnProtocol() tarantool.ProtocolInfo { func ExampleCommitRequest() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -182,22 +181,22 @@ func ExampleCommitRequest() { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"example_commit_key", "example_commit_value"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -207,43 +206,42 @@ func ExampleCommitRequest() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"example_commit_key"}) - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream before commit: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream before commit: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Commit transaction req = tarantool.NewCommitRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Commit: %s", err.Error()) return } - fmt.Printf("Commit transaction: response is %#v\n", resp.Code) + fmt.Printf("Commit transaction: response is %#v\n", data) // Select outside of transaction // example pool has only one rw instance - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after commit: response is %#v\n", resp.Data) + fmt.Printf("Select after commit: response is %#v\n", data) } func ExampleRollbackRequest() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -268,22 +266,22 @@ func ExampleRollbackRequest() { // Begin transaction req = tarantool.NewBeginRequest() - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"example_rollback_key", "example_rollback_value"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -293,43 +291,42 @@ func ExampleRollbackRequest() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"example_rollback_key"}) - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Rollback: %s", err.Error()) return } - fmt.Printf("Rollback transaction: response is %#v\n", resp.Code) + fmt.Printf("Rollback transaction: response is %#v\n", data) // Select outside of transaction // example pool has only one rw instance - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after Rollback: response is %#v\n", resp.Data) + fmt.Printf("Select after Rollback: response is %#v\n", data) } func ExampleBeginRequest_TxnIsolation() { var req tarantool.Request - var resp *tarantool.Response var err error // Tarantool supports streams and interactive transactions since version 2.10.0 @@ -356,22 +353,22 @@ func ExampleBeginRequest_TxnIsolation() { req = tarantool.NewBeginRequest(). TxnIsolation(tarantool.ReadConfirmedLevel). Timeout(500 * time.Millisecond) - resp, err = stream.Do(req).Get() + data, err := stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Begin: %s", err.Error()) return } - fmt.Printf("Begin transaction: response is %#v\n", resp.Code) + fmt.Printf("Begin transaction: response is %#v\n", data) // Insert in stream req = tarantool.NewInsertRequest(spaceName). Tuple([]interface{}{"isolation_level_key", "isolation_level_value"}) - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Insert: %s", err.Error()) return } - fmt.Printf("Insert in stream: response is %#v\n", resp.Code) + fmt.Printf("Insert in stream: response is %#v\n", data) // Select not related to the transaction // while transaction is not committed @@ -381,38 +378,38 @@ func ExampleBeginRequest_TxnIsolation() { Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"isolation_level_key"}) - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select out of stream: response is %#v\n", resp.Data) + fmt.Printf("Select out of stream: response is %#v\n", data) // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select in stream: response is %#v\n", resp.Data) + fmt.Printf("Select in stream: response is %#v\n", data) // Rollback transaction req = tarantool.NewRollbackRequest() - resp, err = stream.Do(req).Get() + data, err = stream.Do(req).Get() if err != nil { fmt.Printf("Failed to Rollback: %s", err.Error()) return } - fmt.Printf("Rollback transaction: response is %#v\n", resp.Code) + fmt.Printf("Rollback transaction: response is %#v\n", data) // Select outside of transaction // example pool has only one rw instance - resp, err = connPool.Do(selectReq, pool.RW).Get() + data, err = connPool.Do(selectReq, pool.RW).Get() if err != nil { fmt.Printf("Failed to Select: %s", err.Error()) return } - fmt.Printf("Select after Rollback: response is %#v\n", resp.Data) + fmt.Printf("Select after Rollback: response is %#v\n", data) } func ExampleConnectorAdapter() { @@ -426,12 +423,10 @@ func ExampleConnectorAdapter() { var connector tarantool.Connector = adapter // Ping an RW instance to check connection. - resp, err := connector.Do(tarantool.NewPingRequest()).Get() - fmt.Println("Ping Code", resp.Code) - fmt.Println("Ping Data", resp.Data) + data, err := connector.Do(tarantool.NewPingRequest()).Get() + fmt.Println("Ping Data", data) fmt.Println("Ping Error", err) // Output: - // Ping Code 0 // Ping Data [] // Ping Error } @@ -473,5 +468,5 @@ func ExampleConnectionPool_CloseGraceful_force() { // Force ConnectionPool.Close()! // ConnectionPool.CloseGraceful() done! // Result: - // connection closed by client (0x4001) + // [] connection closed by client (0x4001) } diff --git a/pool/pooler.go b/pool/pooler.go index 0ff945bbb..6256c2d24 100644 --- a/pool/pooler.go +++ b/pool/pooler.go @@ -19,51 +19,51 @@ type Pooler interface { // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. - Ping(mode Mode) (*tarantool.Response, error) + Ping(mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, - key interface{}, mode ...Mode) (*tarantool.Response, error) + key interface{}, mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. Insert(space interface{}, tuple interface{}, - mode ...Mode) (*tarantool.Response, error) + mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. Replace(space interface{}, tuple interface{}, - mode ...Mode) (*tarantool.Response, error) + mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. Delete(space, index interface{}, key interface{}, - mode ...Mode) (*tarantool.Response, error) + mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. Update(space, index interface{}, key interface{}, ops *tarantool.Operations, - mode ...Mode) (*tarantool.Response, error) + mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. Upsert(space interface{}, tuple interface{}, ops *tarantool.Operations, - mode ...Mode) (*tarantool.Response, error) + mode ...Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. Call(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) + mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. Call16(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) + mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. Call17(functionName string, args interface{}, - mode Mode) (*tarantool.Response, error) + mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. Eval(expr string, args interface{}, - mode Mode) (*tarantool.Response, error) + mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. Execute(expr string, args interface{}, - mode Mode) (*tarantool.Response, error) + mode Mode) (tarantool.Response, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. diff --git a/prepared.go b/prepared.go index 8f2d95519..f4fc1cdf1 100644 --- a/prepared.go +++ b/prepared.go @@ -42,17 +42,21 @@ func fillExecutePrepared(enc *msgpack.Encoder, stmt Prepared, args interface{}) } // NewPreparedFromResponse constructs a Prepared object. -func NewPreparedFromResponse(conn *Connection, resp *Response) (*Prepared, error) { +func NewPreparedFromResponse(conn *Connection, resp Response) (*Prepared, error) { if resp == nil { return nil, fmt.Errorf("passed nil response") } - if resp.Data == nil { + data, err := resp.Decode() + if err != nil { + return nil, fmt.Errorf("decode response body error: %s", err.Error()) + } + if data == nil { return nil, fmt.Errorf("response Data is nil") } - if len(resp.Data) == 0 { + if len(data) == 0 { return nil, fmt.Errorf("response Data format is wrong") } - stmt, ok := resp.Data[0].(*Prepared) + stmt, ok := data[0].(*Prepared) if !ok { return nil, fmt.Errorf("response Data format is wrong") } diff --git a/queue/queue.go b/queue/queue.go index 5c0506f3e..99b1a722f 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -213,14 +213,14 @@ func (q *queue) Cfg(opts CfgOpts) error { // Exists checks existence of a tube. func (q *queue) Exists() (bool, error) { cmd := "local name = ... ; return queue.tube[name] ~= nil" - resp, err := q.conn.Do(tarantool.NewEvalRequest(cmd). + data, err := q.conn.Do(tarantool.NewEvalRequest(cmd). Args([]string{q.name}), ).Get() if err != nil { return false, err } - exist := len(resp.Data) != 0 && resp.Data[0].(bool) + exist := len(data) != 0 && data[0].(bool) return exist, nil } @@ -244,11 +244,11 @@ func (q *queue) Identify(u *uuid.UUID) (uuid.UUID, error) { } req := tarantool.NewCallRequest(q.cmds.identify).Args(args) - if resp, err := q.conn.Do(req).Get(); err == nil { - if us, ok := resp.Data[0].(string); ok { + if data, err := q.conn.Do(req).Get(); err == nil { + if us, ok := data[0].(string); ok { return uuid.FromBytes([]byte(us)) } else { - return uuid.UUID{}, fmt.Errorf("unexpected response: %v", resp.Data) + return uuid.UUID{}, fmt.Errorf("unexpected response: %v", data) } } else { return uuid.UUID{}, err @@ -411,31 +411,31 @@ func (q *queue) Delete(taskId uint64) error { // State returns a current queue state. func (q *queue) State() (State, error) { - resp, err := q.conn.Do(tarantool.NewCallRequest(q.cmds.state)).Get() + data, err := q.conn.Do(tarantool.NewCallRequest(q.cmds.state)).Get() if err != nil { return UnknownState, err } - if respState, ok := resp.Data[0].(string); ok { + if respState, ok := data[0].(string); ok { if state, ok := strToState[respState]; ok { return state, nil } - return UnknownState, fmt.Errorf("unknown state: %v", resp.Data[0]) + return UnknownState, fmt.Errorf("unknown state: %v", data[0]) } - return UnknownState, fmt.Errorf("unexpected response: %v", resp.Data) + return UnknownState, fmt.Errorf("unexpected response: %v", data) } // Return the number of tasks in a queue broken down by task_state, and the // number of requests broken down by the type of request. func (q *queue) Statistic() (interface{}, error) { req := tarantool.NewCallRequest(q.cmds.statistics).Args([]interface{}{q.name}) - resp, err := q.conn.Do(req).Get() + data, err := q.conn.Do(req).Get() if err != nil { return nil, err } - if len(resp.Data) != 0 { - return resp.Data[0], nil + if len(data) != 0 { + return data[0], nil } return nil, nil diff --git a/request.go b/request.go index 8c4f4acd2..bdf1c50ea 100644 --- a/request.go +++ b/request.go @@ -260,8 +260,8 @@ func fillWatchOnce(enc *msgpack.Encoder, key string) error { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (conn *Connection) Ping() (*Response, error) { - return conn.Do(NewPingRequest()).Get() +func (conn *Connection) Ping() (Response, error) { + return conn.Do(NewPingRequest()).GetResponse() } // Select performs select to box space. @@ -271,8 +271,8 @@ func (conn *Connection) Ping() (*Response, error) { // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. func (conn *Connection) Select(space, index interface{}, offset, limit uint32, iterator Iter, - key interface{}) (*Response, error) { - return conn.SelectAsync(space, index, offset, limit, iterator, key).Get() + key interface{}) (Response, error) { + return conn.SelectAsync(space, index, offset, limit, iterator, key).GetResponse() } // Insert performs insertion to box space. @@ -282,8 +282,8 @@ func (conn *Connection) Select(space, index interface{}, offset, limit uint32, i // // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. -func (conn *Connection) Insert(space interface{}, tuple interface{}) (*Response, error) { - return conn.InsertAsync(space, tuple).Get() +func (conn *Connection) Insert(space interface{}, tuple interface{}) (Response, error) { + return conn.InsertAsync(space, tuple).GetResponse() } // Replace performs "insert or replace" action to box space. @@ -293,8 +293,8 @@ func (conn *Connection) Insert(space interface{}, tuple interface{}) (*Response, // // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. -func (conn *Connection) Replace(space interface{}, tuple interface{}) (*Response, error) { - return conn.ReplaceAsync(space, tuple).Get() +func (conn *Connection) Replace(space interface{}, tuple interface{}) (Response, error) { + return conn.ReplaceAsync(space, tuple).GetResponse() } // Delete performs deletion of a tuple by key. @@ -304,8 +304,8 @@ func (conn *Connection) Replace(space interface{}, tuple interface{}) (*Response // // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. -func (conn *Connection) Delete(space, index interface{}, key interface{}) (*Response, error) { - return conn.DeleteAsync(space, index, key).Get() +func (conn *Connection) Delete(space, index interface{}, key interface{}) (Response, error) { + return conn.DeleteAsync(space, index, key).GetResponse() } // Update performs update of a tuple by key. @@ -315,8 +315,8 @@ func (conn *Connection) Delete(space, index interface{}, key interface{}) (*Resp // // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. -func (conn *Connection) Update(space, index, key interface{}, ops *Operations) (*Response, error) { - return conn.UpdateAsync(space, index, key, ops).Get() +func (conn *Connection) Update(space, index, key interface{}, ops *Operations) (Response, error) { + return conn.UpdateAsync(space, index, key, ops).GetResponse() } // Upsert performs "update or insert" action of a tuple by key. @@ -326,8 +326,8 @@ func (conn *Connection) Update(space, index, key interface{}, ops *Operations) ( // // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. -func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) (*Response, error) { - return conn.UpsertAsync(space, tuple, ops).Get() +func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) (Response, error) { + return conn.UpsertAsync(space, tuple, ops).GetResponse() } // Call calls registered Tarantool function. @@ -337,8 +337,8 @@ func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) (*Resp // // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. -func (conn *Connection) Call(functionName string, args interface{}) (*Response, error) { - return conn.CallAsync(functionName, args).Get() +func (conn *Connection) Call(functionName string, args interface{}) (Response, error) { + return conn.CallAsync(functionName, args).GetResponse() } // Call16 calls registered Tarantool function. @@ -349,8 +349,8 @@ func (conn *Connection) Call(functionName string, args interface{}) (*Response, // // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. -func (conn *Connection) Call16(functionName string, args interface{}) (*Response, error) { - return conn.Call16Async(functionName, args).Get() +func (conn *Connection) Call16(functionName string, args interface{}) (Response, error) { + return conn.Call16Async(functionName, args).GetResponse() } // Call17 calls registered Tarantool function. @@ -360,8 +360,8 @@ func (conn *Connection) Call16(functionName string, args interface{}) (*Response // // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. -func (conn *Connection) Call17(functionName string, args interface{}) (*Response, error) { - return conn.Call17Async(functionName, args).Get() +func (conn *Connection) Call17(functionName string, args interface{}) (Response, error) { + return conn.Call17Async(functionName, args).GetResponse() } // Eval passes Lua expression for evaluation. @@ -370,8 +370,8 @@ func (conn *Connection) Call17(functionName string, args interface{}) (*Response // // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. -func (conn *Connection) Eval(expr string, args interface{}) (*Response, error) { - return conn.EvalAsync(expr, args).Get() +func (conn *Connection) Eval(expr string, args interface{}) (Response, error) { + return conn.EvalAsync(expr, args).GetResponse() } // Execute passes sql expression to Tarantool for execution. @@ -381,8 +381,8 @@ func (conn *Connection) Eval(expr string, args interface{}) (*Response, error) { // // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. -func (conn *Connection) Execute(expr string, args interface{}) (*Response, error) { - return conn.ExecuteAsync(expr, args).Get() +func (conn *Connection) Execute(expr string, args interface{}) (Response, error) { + return conn.ExecuteAsync(expr, args).GetResponse() } // single used for conn.GetTyped for decode one tuple. @@ -534,7 +534,7 @@ func (conn *Connection) ExecuteTyped(expr string, args interface{}, result interface{}) (SQLInfo, []ColumnMetaData, error) { fut := conn.ExecuteAsync(expr, args) err := fut.GetTyped(&result) - return fut.resp.SQLInfo, fut.resp.MetaData, err + return fut.resp.SQLInfo(), fut.resp.MetaData(), err } // SelectAsync sends select request to Tarantool and returns Future. diff --git a/response.go b/response.go index 0d6d062b8..09145ee9d 100644 --- a/response.go +++ b/response.go @@ -7,17 +7,33 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -type Response struct { - RequestId uint32 - Code uint32 - // Error contains an error message. - Error string - // Data contains deserialized data for untyped requests. - Data []interface{} - // Pos contains a position descriptor of last selected tuple. - Pos []byte - MetaData []ColumnMetaData - SQLInfo SQLInfo +// Response is an interface with operations for the server responses. +type Response interface { + // Header returns a response header. + Header() Header + // Decode decodes a response. + Decode() ([]interface{}, error) + // DecodeTyped decodes a response into a given container res. + DecodeTyped(res interface{}) error + + // Pos returns a position descriptor of the last selected tuple. + Pos() []byte + // MetaData returns meta-data. + MetaData() []ColumnMetaData + // SQLInfo returns sql info. + SQLInfo() SQLInfo +} + +// ConnResponse is a Response interface implementation. +// It is used for all request types. +type ConnResponse struct { + header Header + // data contains deserialized data for untyped requests. + data []interface{} + // pos contains a position descriptor of last selected tuple. + pos []byte + metaData []ColumnMetaData + sqlInfo SQLInfo buf smallBuf } @@ -103,53 +119,58 @@ func (info *SQLInfo) DecodeMsgpack(d *msgpack.Decoder) error { return nil } -func (resp *Response) smallInt(d *msgpack.Decoder) (i int, err error) { - b, err := resp.buf.ReadByte() +func smallInt(d *msgpack.Decoder, buf *smallBuf) (i int, err error) { + b, err := buf.ReadByte() if err != nil { return } if b <= 127 { return int(b), nil } - resp.buf.UnreadByte() + buf.UnreadByte() return d.DecodeInt() } -func (resp *Response) decodeHeader(d *msgpack.Decoder) (err error) { +func decodeHeader(d *msgpack.Decoder, buf *smallBuf) (Header, error) { var l int - d.Reset(&resp.buf) + var err error + d.Reset(buf) if l, err = d.DecodeMapLen(); err != nil { - return + return Header{}, err } + decodedHeader := Header{} for ; l > 0; l-- { var cd int - if cd, err = resp.smallInt(d); err != nil { - return + if cd, err = smallInt(d, buf); err != nil { + return Header{}, err } switch iproto.Key(cd) { case iproto.IPROTO_SYNC: var rid uint64 if rid, err = d.DecodeUint64(); err != nil { - return + return Header{}, err } - resp.RequestId = uint32(rid) + decodedHeader.RequestId = uint32(rid) case iproto.IPROTO_REQUEST_TYPE: var rcode uint64 if rcode, err = d.DecodeUint64(); err != nil { - return + return Header{}, err } - resp.Code = uint32(rcode) + decodedHeader.Code = uint32(rcode) default: if err = d.Skip(); err != nil { - return + return Header{}, err } } } - return nil + return decodedHeader, nil } -func (resp *Response) decodeBody() (err error) { +func (resp *ConnResponse) Decode() ([]interface{}, error) { + var err error if resp.buf.Len() > 2 { + var decodedError string + offset := resp.buf.Offset() defer resp.buf.Seek(offset) @@ -165,67 +186,67 @@ func (resp *Response) decodeBody() (err error) { }) if l, err = d.DecodeMapLen(); err != nil { - return err + return nil, err } for ; l > 0; l-- { var cd int - if cd, err = resp.smallInt(d); err != nil { - return err + if cd, err = smallInt(d, &resp.buf); err != nil { + return nil, err } switch iproto.Key(cd) { case iproto.IPROTO_DATA: var res interface{} var ok bool if res, err = d.DecodeInterface(); err != nil { - return err + return nil, err } - if resp.Data, ok = res.([]interface{}); !ok { - return fmt.Errorf("result is not array: %v", res) + if resp.data, ok = res.([]interface{}); !ok { + return nil, fmt.Errorf("result is not array: %v", res) } case iproto.IPROTO_ERROR: if errorExtendedInfo, err = decodeBoxError(d); err != nil { - return err + return nil, err } case iproto.IPROTO_ERROR_24: - if resp.Error, err = d.DecodeString(); err != nil { - return err + if decodedError, err = d.DecodeString(); err != nil { + return nil, err } case iproto.IPROTO_SQL_INFO: - if err = d.Decode(&resp.SQLInfo); err != nil { - return err + if err = d.Decode(&resp.sqlInfo); err != nil { + return nil, err } case iproto.IPROTO_METADATA: - if err = d.Decode(&resp.MetaData); err != nil { - return err + if err = d.Decode(&resp.metaData); err != nil { + return nil, err } case iproto.IPROTO_STMT_ID: if stmtID, err = d.DecodeUint64(); err != nil { - return err + return nil, err } case iproto.IPROTO_BIND_COUNT: if bindCount, err = d.DecodeUint64(); err != nil { - return err + return nil, err } case iproto.IPROTO_VERSION: if err = d.Decode(&serverProtocolInfo.Version); err != nil { - return err + return nil, err } case iproto.IPROTO_FEATURES: if larr, err = d.DecodeArrayLen(); err != nil { - return err + return nil, err } serverProtocolInfo.Features = make([]iproto.Feature, larr) for i := 0; i < larr; i++ { if err = d.Decode(&feature); err != nil { - return err + return nil, err } serverProtocolInfo.Features[i] = feature } case iproto.IPROTO_AUTH_TYPE: var auth string if auth, err = d.DecodeString(); err != nil { - return err + return nil, err } found := false for _, a := range [...]Auth{ChapSha1Auth, PapSha256Auth} { @@ -235,15 +256,15 @@ func (resp *Response) decodeBody() (err error) { } } if !found { - return fmt.Errorf("unknown auth type %s", auth) + return nil, fmt.Errorf("unknown auth type %s", auth) } case iproto.IPROTO_POSITION: - if resp.Pos, err = d.DecodeBytes(); err != nil { - return fmt.Errorf("unable to decode a position: %w", err) + if resp.pos, err = d.DecodeBytes(); err != nil { + return nil, fmt.Errorf("unable to decode a position: %w", err) } default: if err = d.Skip(); err != nil { - return err + return nil, err } } } @@ -251,31 +272,32 @@ func (resp *Response) decodeBody() (err error) { stmt := &Prepared{ StatementID: PreparedID(stmtID), ParamCount: bindCount, - MetaData: resp.MetaData, + MetaData: resp.metaData, } - resp.Data = []interface{}{stmt} + resp.data = []interface{}{stmt} } // Tarantool may send only version >= 1 if serverProtocolInfo.Version != ProtocolVersion(0) || serverProtocolInfo.Features != nil { if serverProtocolInfo.Version == ProtocolVersion(0) { - return fmt.Errorf("no protocol version provided in Id response") + return nil, fmt.Errorf("no protocol version provided in Id response") } if serverProtocolInfo.Features == nil { - return fmt.Errorf("no features provided in Id response") + return nil, fmt.Errorf("no features provided in Id response") } - resp.Data = []interface{}{serverProtocolInfo} + resp.data = []interface{}{serverProtocolInfo} } - if resp.Code != OkCode && resp.Code != PushCode { - resp.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.Code), resp.Error, errorExtendedInfo} + if decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), decodedError, errorExtendedInfo} } } - return + return resp.data, err } -func (resp *Response) decodeBodyTyped(res interface{}) (err error) { +func (resp *ConnResponse) DecodeTyped(res interface{}) error { + var err error if resp.buf.Len() > 0 { offset := resp.buf.Offset() defer resp.buf.Seek(offset) @@ -292,9 +314,10 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) { if l, err = d.DecodeMapLen(); err != nil { return err } + var decodedError string for ; l > 0; l-- { var cd int - if cd, err = resp.smallInt(d); err != nil { + if cd, err = smallInt(d, &resp.buf); err != nil { return err } switch iproto.Key(cd) { @@ -307,19 +330,19 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) { return err } case iproto.IPROTO_ERROR_24: - if resp.Error, err = d.DecodeString(); err != nil { + if decodedError, err = d.DecodeString(); err != nil { return err } case iproto.IPROTO_SQL_INFO: - if err = d.Decode(&resp.SQLInfo); err != nil { + if err = d.Decode(&resp.sqlInfo); err != nil { return err } case iproto.IPROTO_METADATA: - if err = d.Decode(&resp.MetaData); err != nil { + if err = d.Decode(&resp.metaData); err != nil { return err } case iproto.IPROTO_POSITION: - if resp.Pos, err = d.DecodeBytes(); err != nil { + if resp.pos, err = d.DecodeBytes(); err != nil { return fmt.Errorf("unable to decode a position: %w", err) } default: @@ -328,33 +351,34 @@ func (resp *Response) decodeBodyTyped(res interface{}) (err error) { } } } - if resp.Code != OkCode && resp.Code != PushCode { - resp.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.Code), resp.Error, errorExtendedInfo} + if decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), decodedError, errorExtendedInfo} } } - return + return err } -// String implements Stringer interface. -func (resp *Response) String() (str string) { - if resp.Code == OkCode { - return fmt.Sprintf("<%d OK %v>", resp.RequestId, resp.Data) - } - return fmt.Sprintf("<%d ERR 0x%x %s>", resp.RequestId, resp.Code, resp.Error) +func (resp *ConnResponse) Header() Header { + return resp.header } -// Tuples converts result of Eval and Call to same format -// as other actions returns (i.e. array of arrays). -func (resp *Response) Tuples() (res [][]interface{}) { - res = make([][]interface{}, len(resp.Data)) - for i, t := range resp.Data { - switch t := t.(type) { - case []interface{}: - res[i] = t - default: - res[i] = []interface{}{t} - } +func (resp *ConnResponse) Pos() []byte { + return resp.pos +} + +func (resp *ConnResponse) MetaData() []ColumnMetaData { + return resp.metaData +} + +func (resp *ConnResponse) SQLInfo() SQLInfo { + return resp.sqlInfo +} + +// String implements Stringer interface. +func (resp *ConnResponse) String() (str string) { + if resp.header.Code == OkCode { + return fmt.Sprintf("<%d OK %v>", resp.header.RequestId, resp.data) } - return res + return fmt.Sprintf("<%d ERR 0x%x>", resp.header.RequestId, resp.header.Code) } diff --git a/response_it.go b/response_it.go index 404c68a51..f5a4517e0 100644 --- a/response_it.go +++ b/response_it.go @@ -12,7 +12,9 @@ type ResponseIterator interface { // Next tries to switch to a next Response and returns true if it exists. Next() bool // Value returns a current Response if it exists, nil otherwise. - Value() *Response + Value() Response + // IsPush returns true if the current response is a push response. + IsPush() bool // Err returns error if it happens. Err() error } diff --git a/settings/example_test.go b/settings/example_test.go index 47f8e8c43..0a1dcc9b7 100644 --- a/settings/example_test.go +++ b/settings/example_test.go @@ -31,7 +31,7 @@ func example_connect(dialer tarantool.Dialer, opts tarantool.Opts) *tarantool.Co } func Example_sqlFullColumnNames() { - var resp *tarantool.Response + var resp tarantool.Response var err error var isLess bool @@ -74,8 +74,9 @@ func Example_sqlFullColumnNames() { fmt.Printf("error on select: %v\n", err) return } + metaData := resp.MetaData() // Show response metadata. - fmt.Printf("full column name: %v\n", resp.MetaData[0].FieldName) + fmt.Printf("full column name: %v\n", metaData[0].FieldName) // Disable showing full column names in SQL responses. _, err = conn.Do(settings.NewSQLFullColumnNamesSetRequest(false)).Get() @@ -90,6 +91,7 @@ func Example_sqlFullColumnNames() { fmt.Printf("error on select: %v\n", err) return } + metaData = resp.MetaData() // Show response metadata. - fmt.Printf("short column name: %v\n", resp.MetaData[0].FieldName) + fmt.Printf("short column name: %v\n", metaData[0].FieldName) } diff --git a/settings/tarantool_test.go b/settings/tarantool_test.go index 272693243..6ca64455a 100644 --- a/settings/tarantool_test.go +++ b/settings/tarantool_test.go @@ -52,48 +52,41 @@ func skipIfSQLDeferForeignKeysSettingUnsupported(t *testing.T) { func TestErrorMarshalingEnabledSetting(t *testing.T) { skipIfErrorMarshalingEnabledSettingUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Disable receiving box.error as MP_EXT 3. - resp, err = conn.Do(NewErrorMarshalingEnabledSetRequest(false)).Get() + data, err := conn.Do(NewErrorMarshalingEnabledSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() + data, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", false}}, data) // Get a box.Error value. eval := tarantool.NewEvalRequest("return box.error.new(box.error.UNKNOWN)") - resp, err = conn.Do(eval).Get() + data, err = conn.Do(eval).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.IsType(t, "string", resp.Data[0]) + require.IsType(t, "string", data[0]) // Enable receiving box.error as MP_EXT 3. - resp, err = conn.Do(NewErrorMarshalingEnabledSetRequest(true)).Get() + data, err = conn.Do(NewErrorMarshalingEnabledSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() + data, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", true}}, data) // Get a box.Error value. - resp, err = conn.Do(eval).Get() + data, err = conn.Do(eval).Get() require.Nil(t, err) - require.NotNil(t, resp) - _, ok := resp.Data[0].(*tarantool.BoxError) + _, ok := data[0].(*tarantool.BoxError) require.True(t, ok) } @@ -101,70 +94,63 @@ func TestSQLDefaultEngineSetting(t *testing.T) { // https://github.com/tarantool/tarantool/blob/680990a082374e4790539215f69d9e9ee39c3307/test/sql/engine.test.lua skipIfSettingsUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Set default SQL "CREATE TABLE" engine to "vinyl". - resp, err = conn.Do(NewSQLDefaultEngineSetRequest("vinyl")).Get() + data, err := conn.Do(NewSQLDefaultEngineSetRequest("vinyl")).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.EqualValues(t, []interface{}{[]interface{}{"sql_default_engine", "vinyl"}}, resp.Data) + require.EqualValues(t, []interface{}{[]interface{}{"sql_default_engine", "vinyl"}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() + data, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "vinyl"}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "vinyl"}}, data) // Create a space with "CREATE TABLE". exec := tarantool.NewExecuteRequest("CREATE TABLE T1_VINYL(a INT PRIMARY KEY, b INT, c INT);") - resp, err = conn.Do(exec).Get() + resp, err := conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Check new space engine. eval := tarantool.NewEvalRequest("return box.space['T1_VINYL'].engine") - resp, err = conn.Do(eval).Get() + data, err = conn.Do(eval).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, "vinyl", resp.Data[0]) + require.Equal(t, "vinyl", data[0]) // Set default SQL "CREATE TABLE" engine to "memtx". - resp, err = conn.Do(NewSQLDefaultEngineSetRequest("memtx")).Get() + data, err = conn.Do(NewSQLDefaultEngineSetRequest("memtx")).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() + data, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, data) // Create a space with "CREATE TABLE". exec = tarantool.NewExecuteRequest("CREATE TABLE T2_MEMTX(a INT PRIMARY KEY, b INT, c INT);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Check new space engine. eval = tarantool.NewEvalRequest("return box.space['T2_MEMTX'].engine") - resp, err = conn.Do(eval).Get() + data, err = conn.Do(eval).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, "memtx", resp.Data[0]) + require.Equal(t, "memtx", data[0]) } func TestSQLDeferForeignKeysSetting(t *testing.T) { // https://github.com/tarantool/tarantool/blob/eafadc13425f14446d7aaa49dea67dfc1d5f45e9/test/sql/transitive-transactions.result skipIfSQLDeferForeignKeysSettingUnsupported(t) - var resp *tarantool.Response + var resp tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -172,18 +158,18 @@ func TestSQLDeferForeignKeysSetting(t *testing.T) { // Create a parent space. exec := tarantool.NewExecuteRequest("CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Create a space with reference to the parent space. exec = tarantool.NewExecuteRequest( "CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y));") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) deferEval := ` box.begin() @@ -198,16 +184,14 @@ func TestSQLDeferForeignKeysSetting(t *testing.T) { ` // Disable foreign key constraint checks before commit. - resp, err = conn.Do(NewSQLDeferForeignKeysSetRequest(false)).Get() + data, err := conn.Do(NewSQLDeferForeignKeysSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLDeferForeignKeysGetRequest()).Get() + data, err = conn.Do(NewSQLDeferForeignKeysGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", false}}, data) // Evaluate a scenario when foreign key not exists // on INSERT, but exists on commit. @@ -215,29 +199,26 @@ func TestSQLDeferForeignKeysSetting(t *testing.T) { require.NotNil(t, err) require.ErrorContains(t, err, "Failed to execute SQL statement: FOREIGN KEY constraint failed") - resp, err = conn.Do(NewSQLDeferForeignKeysSetRequest(true)).Get() + data, err = conn.Do(NewSQLDeferForeignKeysSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLDeferForeignKeysGetRequest()).Get() + data, err = conn.Do(NewSQLDeferForeignKeysGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_defer_foreign_keys", true}}, data) // Evaluate a scenario when foreign key not exists // on INSERT, but exists on commit. - resp, err = conn.Do(tarantool.NewEvalRequest(deferEval)).Get() + data, err = conn.Do(tarantool.NewEvalRequest(deferEval)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, true, resp.Data[0]) + require.Equal(t, true, data[0]) } func TestSQLFullColumnNamesSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response + var resp tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -245,61 +226,57 @@ func TestSQLFullColumnNamesSetting(t *testing.T) { // Create a space. exec := tarantool.NewExecuteRequest("CREATE TABLE FKNAME(ID INT PRIMARY KEY, X INT);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO FKNAME VALUES (1, 1);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Disable displaying full column names in metadata. - resp, err = conn.Do(NewSQLFullColumnNamesSetRequest(false)).Get() + data, err := conn.Do(NewSQLFullColumnNamesSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLFullColumnNamesGetRequest()).Get() + data, err = conn.Do(NewSQLFullColumnNamesGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", false}}, data) // Get a data with short column names in metadata. exec = tarantool.NewExecuteRequest("SELECT X FROM FKNAME WHERE ID = 1;") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "X", resp.MetaData[0].FieldName) + require.Equal(t, "X", resp.MetaData()[0].FieldName) // Enable displaying full column names in metadata. - resp, err = conn.Do(NewSQLFullColumnNamesSetRequest(true)).Get() + data, err = conn.Do(NewSQLFullColumnNamesSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLFullColumnNamesGetRequest()).Get() + data, err = conn.Do(NewSQLFullColumnNamesGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, data) // Get a data with full column names in metadata. exec = tarantool.NewExecuteRequest("SELECT X FROM FKNAME WHERE ID = 1;") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "FKNAME.X", resp.MetaData[0].FieldName) + require.Equal(t, "FKNAME.X", resp.MetaData()[0].FieldName) } func TestSQLFullMetadataSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response + var resp tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -307,88 +284,80 @@ func TestSQLFullMetadataSetting(t *testing.T) { // Create a space. exec := tarantool.NewExecuteRequest("CREATE TABLE fmt(id INT PRIMARY KEY, x INT);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO fmt VALUES (1, 1);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Disable displaying additional fields in metadata. - resp, err = conn.Do(NewSQLFullMetadataSetRequest(false)).Get() + data, err := conn.Do(NewSQLFullMetadataSetRequest(false)).Get() require.Nil(t, err) - require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() + data, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, data) // Get a data without additional fields in metadata. exec = tarantool.NewExecuteRequest("SELECT x FROM fmt WHERE id = 1;") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "", resp.MetaData[0].FieldSpan) + require.Equal(t, "", resp.MetaData()[0].FieldSpan) // Enable displaying full column names in metadata. - resp, err = conn.Do(NewSQLFullMetadataSetRequest(true)).Get() + data, err = conn.Do(NewSQLFullMetadataSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() + data, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", true}}, data) // Get a data with additional fields in metadata. exec = tarantool.NewExecuteRequest("SELECT x FROM fmt WHERE id = 1;") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "x", resp.MetaData[0].FieldSpan) + require.Equal(t, "x", resp.MetaData()[0].FieldSpan) } func TestSQLParserDebugSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Disable parser debug mode. - resp, err = conn.Do(NewSQLParserDebugSetRequest(false)).Get() + data, err := conn.Do(NewSQLParserDebugSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLParserDebugGetRequest()).Get() + data, err = conn.Do(NewSQLParserDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", false}}, data) // Enable parser debug mode. - resp, err = conn.Do(NewSQLParserDebugSetRequest(true)).Get() + data, err = conn.Do(NewSQLParserDebugSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLParserDebugGetRequest()).Get() + data, err = conn.Do(NewSQLParserDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", true}}, data) // To test real effect we need a Tarantool instance built with // `-DCMAKE_BUILD_TYPE=Debug`. @@ -398,7 +367,7 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { // https://github.com/tarantool/tarantool/blob/d11fb3061e15faf4e0eb5375fb8056b4e64348ae/test/sql-tap/triggerC.test.lua skipIfSettingsUnsupported(t) - var resp *tarantool.Response + var resp tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -406,39 +375,37 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { // Create a space. exec := tarantool.NewExecuteRequest("CREATE TABLE rec(id INTEGER PRIMARY KEY, a INT, b INT);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO rec VALUES(1, 1, 2);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Create a recursive trigger (with infinite depth). exec = tarantool.NewExecuteRequest(` CREATE TRIGGER tr12 AFTER UPDATE ON rec FOR EACH ROW BEGIN UPDATE rec SET a=new.a+1, b=new.b+1; END;`) - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Enable SQL recursive triggers. - resp, err = conn.Do(NewSQLRecursiveTriggersSetRequest(true)).Get() + data, err := conn.Do(NewSQLRecursiveTriggersSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() + data, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", true}}, data) // Trigger the recursion. exec = tarantool.NewExecuteRequest("UPDATE rec SET a=a+1, b=b+1;") @@ -448,29 +415,27 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { "Failed to execute SQL statement: too many levels of trigger recursion") // Disable SQL recursive triggers. - resp, err = conn.Do(NewSQLRecursiveTriggersSetRequest(false)).Get() + data, err = conn.Do(NewSQLRecursiveTriggersSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() + data, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", false}}, data) // Trigger the recursion. exec = tarantool.NewExecuteRequest("UPDATE rec SET a=a+1, b=b+1;") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) } func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response + var resp tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -478,37 +443,35 @@ func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { // Create a space. exec := tarantool.NewExecuteRequest("CREATE TABLE data(id STRING PRIMARY KEY);") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO data VALUES('1');") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) exec = tarantool.NewExecuteRequest("INSERT INTO data VALUES('2');") - resp, err = conn.Do(exec).Get() + resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) // Disable reverse order in unordered selects. - resp, err = conn.Do(NewSQLReverseUnorderedSelectsSetRequest(false)).Get() + data, err := conn.Do(NewSQLReverseUnorderedSelectsSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) require.Equal(t, []interface{}{[]interface{}{"sql_reverse_unordered_selects", false}}, - resp.Data) + data) // Fetch current setting value. - resp, err = conn.Do(NewSQLReverseUnorderedSelectsGetRequest()).Get() + data, err = conn.Do(NewSQLReverseUnorderedSelectsGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) require.Equal(t, []interface{}{[]interface{}{"sql_reverse_unordered_selects", false}}, - resp.Data) + data) // Select multiple records. query := "SELECT * FROM seqscan data;" @@ -518,66 +481,57 @@ func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { query = "SELECT * FROM data;" } - resp, err = conn.Do(tarantool.NewExecuteRequest(query)).Get() + data, err = conn.Do(tarantool.NewExecuteRequest(query)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.EqualValues(t, []interface{}{"1"}, resp.Data[0]) - require.EqualValues(t, []interface{}{"2"}, resp.Data[1]) + require.EqualValues(t, []interface{}{"1"}, data[0]) + require.EqualValues(t, []interface{}{"2"}, data[1]) // Enable reverse order in unordered selects. - resp, err = conn.Do(NewSQLReverseUnorderedSelectsSetRequest(true)).Get() + data, err = conn.Do(NewSQLReverseUnorderedSelectsSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) require.Equal(t, []interface{}{[]interface{}{"sql_reverse_unordered_selects", true}}, - resp.Data) + data) // Fetch current setting value. - resp, err = conn.Do(NewSQLReverseUnorderedSelectsGetRequest()).Get() + data, err = conn.Do(NewSQLReverseUnorderedSelectsGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) require.Equal(t, []interface{}{[]interface{}{"sql_reverse_unordered_selects", true}}, - resp.Data) + data) // Select multiple records. - resp, err = conn.Do(tarantool.NewExecuteRequest(query)).Get() + data, err = conn.Do(tarantool.NewExecuteRequest(query)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.EqualValues(t, []interface{}{"2"}, resp.Data[0]) - require.EqualValues(t, []interface{}{"1"}, resp.Data[1]) + require.EqualValues(t, []interface{}{"2"}, data[0]) + require.EqualValues(t, []interface{}{"1"}, data[1]) } func TestSQLSelectDebugSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Disable select debug mode. - resp, err = conn.Do(NewSQLSelectDebugSetRequest(false)).Get() + data, err := conn.Do(NewSQLSelectDebugSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() + data, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", false}}, data) // Enable select debug mode. - resp, err = conn.Do(NewSQLSelectDebugSetRequest(true)).Get() + data, err = conn.Do(NewSQLSelectDebugSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() + data, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", true}}, data) // To test real effect we need a Tarantool instance built with // `-DCMAKE_BUILD_TYPE=Debug`. @@ -586,35 +540,30 @@ func TestSQLSelectDebugSetting(t *testing.T) { func TestSQLVDBEDebugSetting(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Disable VDBE debug mode. - resp, err = conn.Do(NewSQLVDBEDebugSetRequest(false)).Get() + data, err := conn.Do(NewSQLVDBEDebugSetRequest(false)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", false}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() + data, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", false}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", false}}, data) // Enable VDBE debug mode. - resp, err = conn.Do(NewSQLVDBEDebugSetRequest(true)).Get() + data, err = conn.Do(NewSQLVDBEDebugSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", true}}, data) // Fetch current setting value. - resp, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() + data, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", true}}, data) // To test real effect we need a Tarantool instance built with // `-DCMAKE_BUILD_TYPE=Debug`. @@ -623,28 +572,24 @@ func TestSQLVDBEDebugSetting(t *testing.T) { func TestSessionSettings(t *testing.T) { skipIfSettingsUnsupported(t) - var resp *tarantool.Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Set some settings values. - resp, err = conn.Do(NewSQLDefaultEngineSetRequest("memtx")).Get() + data, err := conn.Do(NewSQLDefaultEngineSetRequest("memtx")).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, data) - resp, err = conn.Do(NewSQLFullColumnNamesSetRequest(true)).Get() + data, err = conn.Do(NewSQLFullColumnNamesSetRequest(true)).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, resp.Data) + require.Equal(t, []interface{}{[]interface{}{"sql_full_column_names", true}}, data) // Fetch current settings values. - resp, err = conn.Do(NewSessionSettingsGetRequest()).Get() + data, err = conn.Do(NewSessionSettingsGetRequest()).Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Subset(t, resp.Data, + require.Subset(t, data, []interface{}{ []interface{}{"sql_default_engine", "memtx"}, []interface{}{"sql_full_column_names", true}, diff --git a/shutdown_test.go b/shutdown_test.go index 80996e3a9..b3a09eff0 100644 --- a/shutdown_test.go +++ b/shutdown_test.go @@ -47,7 +47,6 @@ var evalBody = ` ` func testGracefulShutdown(t *testing.T, conn *Connection, inst *test_helpers.TarantoolInstance) { - var resp *Response var err error // Set a big timeout so it would be easy to differ @@ -102,10 +101,9 @@ func testGracefulShutdown(t *testing.T, conn *Connection, inst *test_helpers.Tar require.Nil(t, err) // Check that requests started before the shutdown finish successfully. - resp, err = fut.Get() + data, err := fut.Get() require.Nil(t, err) - require.NotNil(t, resp) - require.Equal(t, resp.Data, []interface{}{evalMsg}) + require.Equal(t, data, []interface{}{evalMsg}) // Wait until server go down. // Server will go down only when it process all requests from our connection diff --git a/tarantool_test.go b/tarantool_test.go index 4d3f193f1..aa3f248f9 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -759,12 +759,12 @@ func TestFutureMultipleGetGetTyped(t *testing.T) { } if get { - resp, err := fut.Get() + data, err := fut.Get() if err != nil { t.Errorf("Failed to call Get(): %s", err) } - if val, ok := resp.Data[0].(string); !ok || val != "11" { - t.Errorf("Wrong Get() result: %v", resp.Data) + if val, ok := data[0].(string); !ok || val != "11" { + t.Errorf("Wrong Get() result: %v", data) } } else { tpl := struct { @@ -821,7 +821,7 @@ func TestFutureMultipleGetTypedWithError(t *testing.T) { /////////////////// func TestClient(t *testing.T) { - var resp *Response + var resp Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -835,7 +835,7 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Ping") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } @@ -847,13 +847,17 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Insert") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 1 { + data, err := resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 1 { t.Errorf("Response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Insert") } else { if len(tpl) != 3 { @@ -870,7 +874,8 @@ func TestClient(t *testing.T) { if tntErr, ok := err.(Error); !ok || tntErr.Code != iproto.ER_TUPLE_FOUND { t.Errorf("Expected %s but got: %v", iproto.ER_TUPLE_FOUND, err) } - if len(resp.Data) != 0 { + data, _ = resp.Decode() + if len(data) != 0 { t.Errorf("Response Body len != 0") } @@ -882,13 +887,17 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Delete") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 1 { t.Errorf("Response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Delete") } else { if len(tpl) != 3 { @@ -908,10 +917,14 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Delete") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 0 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 0 { t.Errorf("Response Data len != 0") } @@ -923,7 +936,7 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Replace") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } resp, err = conn.Replace(spaceNo, []interface{}{uint(2), "hi", "planet"}) @@ -933,10 +946,14 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Replace (duplicate)") } - if len(resp.Data) != 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 1 { t.Errorf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Replace") } else { if len(tpl) != 3 { @@ -959,13 +976,17 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Update") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 1 { t.Errorf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Update") } else { if len(tpl) != 2 { @@ -988,7 +1009,7 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Upsert (insert)") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } resp, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, @@ -1006,11 +1027,12 @@ func TestClient(t *testing.T) { if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if resp.Code != 0 { - t.Errorf("Failed to replace") + _, err := resp.Decode() + if err != nil { + t.Errorf("Failed to replace: %s", err.Error()) } } resp, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}) @@ -1020,13 +1042,17 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Select") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 1 { t.Errorf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != 10 { @@ -1045,10 +1071,14 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Select") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 0 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != 0 { t.Errorf("Response Data len != 0") } @@ -1114,10 +1144,14 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Call16") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) < 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") } @@ -1126,22 +1160,30 @@ func TestClient(t *testing.T) { if err != nil { t.Errorf("Failed to use Call16") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if val, ok := data[0].([]interface{})[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } resp, err = conn.Call17("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if val, ok := resp.Data[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if val, ok := data[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } // Eval @@ -1152,13 +1194,17 @@ func TestClient(t *testing.T) { if resp == nil { t.Fatalf("Response is nil after Eval") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) < 1 { + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") } - if val, err := test_helpers.ConvertUint64(resp.Data[0]); err != nil || val != 11 { + if val, err := test_helpers.ConvertUint64(data[0]); err != nil || val != 11 { t.Errorf("5 + 6 == 11, but got %v", val) } } @@ -1196,15 +1242,13 @@ func TestClientSessionPush(t *testing.T) { // It will wait a response before iteration. fut1 := conn.Call17Async("push_func", []interface{}{pushMax}) // Future.Get ignores push messages. - resp, err := fut1.Get() + data, err := fut1.Get() if err != nil { t.Errorf("Failed to Call17: %s", err.Error()) - } else if resp == nil { - t.Errorf("Response is nil after CallAsync") - } else if len(resp.Data) < 1 { + } else if len(data) < 1 { t.Errorf("Response.Data is empty after Call17Async") - } else if val, err := test_helpers.ConvertUint64(resp.Data[0]); err != nil || val != pushMax { - t.Errorf("Result is not %d: %v", pushMax, resp.Data) + } else if val, err := test_helpers.ConvertUint64(data[0]); err != nil || val != pushMax { + t.Errorf("Result is not %d: %v", pushMax, data) } // It will will be iterated with a timeout. @@ -1216,60 +1260,41 @@ func TestClientSessionPush(t *testing.T) { } for i := 0; i < len(its); i++ { - pushCnt := uint64(0) - respCnt := uint64(0) - it = its[i] for it.Next() { - resp = it.Value() + resp := it.Value() if resp == nil { t.Errorf("Response is empty after it.Next() == true") break } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) < 1 { - t.Errorf("Response.Data is empty after CallAsync") + data, err := resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) break } - if resp.Code == PushCode { - pushCnt += 1 - val, err := test_helpers.ConvertUint64(resp.Data[0]) - if err != nil || val != pushCnt { - t.Errorf("Unexpected push data = %v", resp.Data) - } - } else { - respCnt += 1 - val, err := test_helpers.ConvertUint64(resp.Data[0]) - if err != nil || val != pushMax { - t.Errorf("Result is not %d: %v", pushMax, resp.Data) - } + if len(data) < 1 { + t.Errorf("Response.Data is empty after CallAsync") + break } } if err = it.Err(); err != nil { t.Errorf("An unexpected iteration error: %s", err.Error()) } - - if pushCnt != pushMax { - t.Errorf("Expect %d pushes but got %d", pushMax, pushCnt) - } - - if respCnt != 1 { - t.Errorf("Expect %d responses but got %d", 1, respCnt) - } } // We can collect original responses after iterations. for _, fut := range []*Future{fut0, fut1, fut2} { - resp, err := fut.Get() + data, err := fut.Get() if err != nil { t.Errorf("Unable to call fut.Get(): %s", err) } - val, err := test_helpers.ConvertUint64(resp.Data[0]) + val, err := test_helpers.ConvertUint64(data[0]) if err != nil || val != pushMax { - t.Errorf("Result is not %d: %v", pushMax, resp.Data) + t.Errorf("Result is not %d: %v", pushMax, data) } tpl := struct { @@ -1311,9 +1336,11 @@ func TestSQL(t *testing.T) { test_helpers.SkipIfSQLUnsupported(t) type testCase struct { - Query string - Args interface{} - Resp Response + Query string + Args interface{} + sqlInfo SQLInfo + data []interface{} + metaData []ColumnMetaData } selectSpanDifQuery := selectSpanDifQueryNew @@ -1327,20 +1354,16 @@ func TestSQL(t *testing.T) { { createTableQuery, []interface{}{}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { insertQuery, []interface{}{"1", "test"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { selectNamedQuery, @@ -1348,119 +1371,100 @@ func TestSQL(t *testing.T) { "ID": "1", "NAME": "test", }, - Response{ - SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{[]interface{}{"1", "test"}}, - MetaData: []ColumnMetaData{ - {FieldType: "string", FieldName: "ID"}, - {FieldType: "string", FieldName: "NAME"}}, - }, + SQLInfo{AffectedCount: 0}, + []interface{}{[]interface{}{"1", "test"}}, + []ColumnMetaData{ + {FieldType: "string", FieldName: "ID"}, + {FieldType: "string", FieldName: "NAME"}}, }, { selectPosQuery, []interface{}{"1", "test"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{[]interface{}{"1", "test"}}, - MetaData: []ColumnMetaData{ - {FieldType: "string", FieldName: "ID"}, - {FieldType: "string", FieldName: "NAME"}}, - }, + SQLInfo{AffectedCount: 0}, + []interface{}{[]interface{}{"1", "test"}}, + []ColumnMetaData{ + {FieldType: "string", FieldName: "ID"}, + {FieldType: "string", FieldName: "NAME"}}, }, { updateQuery, []interface{}{"test_test", "1"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { enableFullMetaDataQuery, []interface{}{}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { selectSpanDifQuery, []interface{}{"test_test"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{[]interface{}{"11", "test_test", "1"}}, - MetaData: []ColumnMetaData{ - { - FieldType: "string", - FieldName: "COLUMN_1", - FieldIsNullable: false, - FieldIsAutoincrement: false, - FieldSpan: "ID||ID", - }, - { - FieldType: "string", - FieldName: "NAME", - FieldIsNullable: true, - FieldIsAutoincrement: false, - FieldSpan: "NAME", - FieldCollation: "unicode", - }, - { - FieldType: "string", - FieldName: "ID", - FieldIsNullable: false, - FieldIsAutoincrement: false, - FieldSpan: "ID", - FieldCollation: "", - }, - }}, + SQLInfo{AffectedCount: 0}, + []interface{}{[]interface{}{"11", "test_test", "1"}}, + []ColumnMetaData{ + { + FieldType: "string", + FieldName: "COLUMN_1", + FieldIsNullable: false, + FieldIsAutoincrement: false, + FieldSpan: "ID||ID", + }, + { + FieldType: "string", + FieldName: "NAME", + FieldIsNullable: true, + FieldIsAutoincrement: false, + FieldSpan: "NAME", + FieldCollation: "unicode", + }, + { + FieldType: "string", + FieldName: "ID", + FieldIsNullable: false, + FieldIsAutoincrement: false, + FieldSpan: "ID", + FieldCollation: "", + }, + }, }, { alterTableQuery, []interface{}{}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 0}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 0}, + []interface{}{}, + nil, }, { insertIncrQuery, []interface{}{"2", "test_2"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1, InfoAutoincrementIds: []uint64{1}}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1, InfoAutoincrementIds: []uint64{1}}, + []interface{}{}, + nil, }, { deleteQuery, []interface{}{"test_2"}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { dropQuery, []interface{}{}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, { disableFullMetaDataQuery, []interface{}{}, - Response{ - SQLInfo: SQLInfo{AffectedCount: 1}, - Data: []interface{}{}, - MetaData: nil, - }, + SQLInfo{AffectedCount: 1}, + []interface{}{}, + nil, }, } @@ -1471,23 +1475,26 @@ func TestSQL(t *testing.T) { resp, err := conn.Execute(test.Query, test.Args) assert.NoError(t, err, "Failed to Execute, query: %s", test.Query) assert.NotNil(t, resp, "Response is nil after Execute\nQuery number: %d", i) - for j := range resp.Data { - assert.Equal(t, resp.Data[j], test.Resp.Data[j], "Response data is wrong") + data, err := resp.Decode() + assert.Nil(t, err, "Failed to Decode") + for j := range data { + assert.Equal(t, data[j], test.data[j], "Response data is wrong") } - assert.Equal(t, resp.SQLInfo.AffectedCount, test.Resp.SQLInfo.AffectedCount, + assert.Equal(t, resp.SQLInfo().AffectedCount, test.sqlInfo.AffectedCount, "Affected count is wrong") errorMsg := "Response Metadata is wrong" - for j := range resp.MetaData { - assert.Equal(t, resp.MetaData[j].FieldIsAutoincrement, - test.Resp.MetaData[j].FieldIsAutoincrement, errorMsg) - assert.Equal(t, resp.MetaData[j].FieldIsNullable, - test.Resp.MetaData[j].FieldIsNullable, errorMsg) - assert.Equal(t, resp.MetaData[j].FieldCollation, - test.Resp.MetaData[j].FieldCollation, errorMsg) - assert.Equal(t, resp.MetaData[j].FieldName, test.Resp.MetaData[j].FieldName, errorMsg) - assert.Equal(t, resp.MetaData[j].FieldSpan, test.Resp.MetaData[j].FieldSpan, errorMsg) - assert.Equal(t, resp.MetaData[j].FieldType, test.Resp.MetaData[j].FieldType, errorMsg) + metaData := resp.MetaData() + for j := range metaData { + assert.Equal(t, metaData[j].FieldIsAutoincrement, + test.metaData[j].FieldIsAutoincrement, errorMsg) + assert.Equal(t, metaData[j].FieldIsNullable, + test.metaData[j].FieldIsNullable, errorMsg) + assert.Equal(t, metaData[j].FieldCollation, + test.metaData[j].FieldCollation, errorMsg) + assert.Equal(t, metaData[j].FieldName, test.metaData[j].FieldName, errorMsg) + assert.Equal(t, metaData[j].FieldSpan, test.metaData[j].FieldSpan, errorMsg) + assert.Equal(t, metaData[j].FieldType, test.metaData[j].FieldType, errorMsg) } } } @@ -1522,7 +1529,7 @@ func TestSQLBindings(t *testing.T) { 1: "test", } - var resp *Response + var resp Response conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() @@ -1575,13 +1582,18 @@ func TestSQLBindings(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if reflect.DeepEqual(resp.Data[0], []interface{}{1, testData[1]}) { + data, err := resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) + } + if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with named arguments failed") } - if resp.MetaData[0].FieldType != "unsigned" || - resp.MetaData[0].FieldName != "NAME0" || - resp.MetaData[1].FieldType != "string" || - resp.MetaData[1].FieldName != "NAME1" { + metaData := resp.MetaData() + if metaData[0].FieldType != "unsigned" || + metaData[0].FieldName != "NAME0" || + metaData[1].FieldType != "string" || + metaData[1].FieldName != "NAME1" { t.Error("Wrong metadata") } } @@ -1593,13 +1605,18 @@ func TestSQLBindings(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if reflect.DeepEqual(resp.Data[0], []interface{}{1, testData[1]}) { + data, err := resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) + } + if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") } - if resp.MetaData[0].FieldType != "unsigned" || - resp.MetaData[0].FieldName != "NAME0" || - resp.MetaData[1].FieldType != "string" || - resp.MetaData[1].FieldName != "NAME1" { + metaData := resp.MetaData() + if metaData[0].FieldType != "unsigned" || + metaData[0].FieldName != "NAME0" || + metaData[1].FieldType != "string" || + metaData[1].FieldName != "NAME1" { t.Error("Wrong metadata") } @@ -1610,13 +1627,18 @@ func TestSQLBindings(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if reflect.DeepEqual(resp.Data[0], []interface{}{1, testData[1]}) { + data, err = resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) + } + if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") } - if resp.MetaData[0].FieldType != "unsigned" || - resp.MetaData[0].FieldName != "NAME0" || - resp.MetaData[1].FieldType != "string" || - resp.MetaData[1].FieldName != "NAME1" { + metaData = resp.MetaData() + if metaData[0].FieldType != "unsigned" || + metaData[0].FieldName != "NAME0" || + metaData[1].FieldType != "string" || + metaData[1].FieldName != "NAME1" { t.Error("Wrong metadata") } } @@ -1624,7 +1646,7 @@ func TestSQLBindings(t *testing.T) { func TestStressSQL(t *testing.T) { test_helpers.SkipIfSQLUnsupported(t) - var resp *Response + var resp Response conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() @@ -1636,11 +1658,8 @@ func TestStressSQL(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Code != 0 { - t.Fatalf("Failed to Execute: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 1 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 1 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } // create table with the same name @@ -1652,11 +1671,8 @@ func TestStressSQL(t *testing.T) { t.Fatal("Response is nil after Execute") } - if iproto.Error(resp.Code) != iproto.ER_SPACE_EXISTS { - t.Fatalf("Unexpected response code: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } // execute with nil argument @@ -1667,11 +1683,8 @@ func TestStressSQL(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Code == 0 { - t.Fatalf("Unexpected response code: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } // execute with zero string @@ -1682,11 +1695,8 @@ func TestStressSQL(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Code == 0 { - t.Fatalf("Unexpected response code: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } // drop table query @@ -1697,11 +1707,8 @@ func TestStressSQL(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Code != 0 { - t.Fatalf("Failed to Execute: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 1 { - t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 1 { + t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo().AffectedCount) } // drop the same table @@ -1712,11 +1719,8 @@ func TestStressSQL(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Code == 0 { - t.Fatalf("Unexpected response code: %d", resp.Code) - } - if resp.SQLInfo.AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } } @@ -1734,30 +1738,29 @@ func TestNewPrepared(t *testing.T) { executeReq := NewExecutePreparedRequest(stmt) unprepareReq := NewUnprepareRequest(stmt) - resp, err := conn.Do(executeReq.Args([]interface{}{1, "test"})).Get() + resp, err := conn.Do(executeReq.Args([]interface{}{1, "test"})).GetResponse() if err != nil { t.Errorf("failed to execute prepared: %v", err) } - if resp.Code != OkCode { - t.Errorf("failed to execute prepared: code %d", resp.Code) + data, err := resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) } - if reflect.DeepEqual(resp.Data[0], []interface{}{1, "test"}) { + if reflect.DeepEqual(data[0], []interface{}{1, "test"}) { t.Error("Select with named arguments failed") } - if resp.MetaData[0].FieldType != "unsigned" || - resp.MetaData[0].FieldName != "NAME0" || - resp.MetaData[1].FieldType != "string" || - resp.MetaData[1].FieldName != "NAME1" { + metaData := resp.MetaData() + if metaData[0].FieldType != "unsigned" || + metaData[0].FieldName != "NAME0" || + metaData[1].FieldType != "string" || + metaData[1].FieldName != "NAME1" { t.Error("Wrong metadata") } - resp, err = conn.Do(unprepareReq).Get() + _, err = conn.Do(unprepareReq).Get() if err != nil { t.Errorf("failed to unprepare prepared statement: %v", err) } - if resp.Code != OkCode { - t.Errorf("failed to unprepare prepared statement: code %d", resp.Code) - } _, err = conn.Do(unprepareReq).Get() if err == nil { @@ -1772,21 +1775,18 @@ func TestNewPrepared(t *testing.T) { require.Contains(t, err.Error(), "Prepared statement with id") prepareReq := NewPrepareRequest(selectNamedQuery2) - resp, err = conn.Do(prepareReq).Get() + data, err = conn.Do(prepareReq).Get() if err != nil { t.Errorf("failed to prepare: %v", err) } - if resp.Data == nil { + if data == nil { t.Errorf("failed to prepare: Data is nil") } - if resp.Code != OkCode { - t.Errorf("failed to unprepare prepared statement: code %d", resp.Code) - } - if len(resp.Data) == 0 { + if len(data) == 0 { t.Errorf("failed to prepare: response Data has no elements") } - stmt, ok := resp.Data[0].(*Prepared) + stmt, ok := data[0].(*Prepared) if !ok { t.Errorf("failed to prepare: failed to cast the response Data to Prepared object") } @@ -1830,13 +1830,10 @@ func TestConnection_SetSchema_Changes(t *testing.T) { req := NewInsertRequest(spaceName) req.Tuple([]interface{}{uint(1010), "Tarantool"}) - resp, err := conn.Do(req).Get() + _, err := conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) - } s, err := GetSchema(conn) if err != nil { @@ -1850,41 +1847,12 @@ func TestConnection_SetSchema_Changes(t *testing.T) { reqS := NewSelectRequest(spaceName) reqS.Key([]interface{}{uint(1010)}) - resp, err = conn.Do(reqS).Get() + data, err := conn.Do(reqS).Get() if err != nil { t.Fatalf("failed to Select: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("failed to Select: wrong code returned %d", resp.Code) - } - if resp.Data[0].([]interface{})[1] != "Tarantool" { - t.Errorf("wrong Select body: %v", resp.Data) - } -} - -func TestNewPreparedFromResponse(t *testing.T) { - var ( - ErrNilResponsePassed = fmt.Errorf("passed nil response") - ErrNilResponseData = fmt.Errorf("response Data is nil") - ErrWrongDataFormat = fmt.Errorf("response Data format is wrong") - ) - testConn := &Connection{} - testCases := []struct { - name string - resp *Response - expectedError error - }{ - {"ErrNilResponsePassed", nil, ErrNilResponsePassed}, - {"ErrNilResponseData", &Response{Data: nil}, ErrNilResponseData}, - {"ErrWrongDataFormat", &Response{Data: []interface{}{}}, ErrWrongDataFormat}, - {"ErrWrongDataFormat", &Response{Data: []interface{}{"test"}}, ErrWrongDataFormat}, - {"nil", &Response{Data: []interface{}{&Prepared{}}}, nil}, - } - for _, testCase := range testCases { - t.Run("Expecting error "+testCase.name, func(t *testing.T) { - _, err := NewPreparedFromResponse(testConn, testCase.resp) - assert.Equal(t, err, testCase.expectedError) - }) + if data[0].([]interface{})[1] != "Tarantool" { + t.Errorf("wrong Select body: %v", data) } } @@ -2075,7 +2043,7 @@ func TestSchema_IsNullable(t *testing.T) { } func TestClientNamed(t *testing.T) { - var resp *Response + var resp Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -2086,8 +2054,8 @@ func TestClientNamed(t *testing.T) { if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp.Code != 0 { - t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) + if resp == nil { + t.Errorf("Response is nil after Insert") } // Delete @@ -2145,8 +2113,8 @@ func TestClientNamed(t *testing.T) { if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp.Code != 0 { - t.Errorf("Failed to Replace: wrong code returned %d", resp.Code) + if resp == nil { + t.Errorf("Response is nil after Replace") } } resp, err = conn.Select(spaceName, indexName, 0, 1, IterEq, []interface{}{uint(1010)}) @@ -2170,9 +2138,8 @@ func TestClientNamed(t *testing.T) { func TestClientRequestObjects(t *testing.T) { var ( - req Request - resp *Response - err error + req Request + err error ) conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -2180,14 +2147,11 @@ func TestClientRequestObjects(t *testing.T) { // Ping req = NewPingRequest() - resp, err = conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Ping: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Ping") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Errorf("Response Body len != 0") } @@ -2200,23 +2164,14 @@ func TestClientRequestObjects(t *testing.T) { for i := 1010; i < 1020; i++ { req = NewInsertRequest(spaceName). Tuple([]interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Insert") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { - t.Fatalf("Response data is nil after Insert") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Insert") } else { if len(tpl) != 3 { @@ -2238,23 +2193,14 @@ func TestClientRequestObjects(t *testing.T) { for i := 1015; i < 1020; i++ { req = NewReplaceRequest(spaceName). Tuple([]interface{}{uint(i), fmt.Sprintf("val %d", i), "blar"}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Replace: %s", err.Error()) + t.Fatalf("Failed to Decode: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Replace") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { - t.Fatalf("Response data is nil after Replace") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Replace") } else { if len(tpl) != 3 { @@ -2275,23 +2221,17 @@ func TestClientRequestObjects(t *testing.T) { // Delete req = NewDeleteRequest(spaceName). Key([]interface{}{uint(1016)}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Delete: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Delete") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { + if data == nil { t.Fatalf("Response data is nil after Delete") } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Delete") } else { if len(tpl) != 3 { @@ -2312,23 +2252,17 @@ func TestClientRequestObjects(t *testing.T) { req = NewUpdateRequest(spaceName). Index(indexName). Key([]interface{}{uint(1010)}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to Update: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Update") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { + if data == nil { t.Fatalf("Response data is nil after Update") } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Update") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != uint64(1010) { @@ -2347,23 +2281,14 @@ func TestClientRequestObjects(t *testing.T) { Index(indexName). Key([]interface{}{uint(1010)}). Operations(NewOperations().Assign(1, "bye").Insert(2, 1)) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to Update: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Update") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { - t.Fatalf("Response data is nil after Update") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != 1010 { @@ -2380,20 +2305,11 @@ func TestClientRequestObjects(t *testing.T) { // Upsert without operations. req = NewUpsertRequest(spaceNo). Tuple([]interface{}{uint(1010), "hi", "hi"}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to Upsert (update): %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Upsert (update)") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { - t.Fatalf("Response data is nil after Upsert") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } @@ -2401,65 +2317,44 @@ func TestClientRequestObjects(t *testing.T) { req = NewUpsertRequest(spaceNo). Tuple([]interface{}{uint(1010), "hi", "hi"}). Operations(NewOperations().Assign(2, "bye")) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to Upsert (update): %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Upsert (update)") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if resp.Data == nil { - t.Fatalf("Response data is nil after Upsert") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } // Call16 vs Call17 req = NewCall16Request("simple_concat").Args([]interface{}{"1"}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call") } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if val, ok := resp.Data[0].([]interface{})[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + if val, ok := data[0].([]interface{})[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } // Call17 req = NewCall17Request("simple_concat").Args([]interface{}{"1"}) - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call17") } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if val, ok := resp.Data[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + if val, ok := data[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } // Eval req = NewEvalRequest("return 5 + 6") - resp, err = conn.Do(req).Get() + data, err = conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Eval: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Eval") - } - if resp.Pos != nil { - t.Errorf("Response should not have a position") - } - if len(resp.Data) < 1 { + if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") } - if val, err := test_helpers.ConvertUint64(resp.Data[0]); err != nil || val != 11 { + if val, err := test_helpers.ConvertUint64(data[0]); err != nil || val != 11 { t.Errorf("5 + 6 == 11, but got %v", val) } @@ -2473,45 +2368,47 @@ func TestClientRequestObjects(t *testing.T) { } req = NewExecuteRequest(createTableQuery) - resp, err = conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 0 { - t.Fatalf("Response Body len != 0") + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Execute: %d", resp.Code) + if len(data) != 0 { + t.Fatalf("Response Body len != 0") } - if resp.SQLInfo.AffectedCount != 1 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 1 { + t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) } req = NewExecuteRequest(dropQuery2) - resp, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Pos != nil { + if resp.Pos() != nil { t.Errorf("Response should not have a position") } - if len(resp.Data) != 0 { - t.Fatalf("Response Body len != 0") + data, err = resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Execute: %d", resp.Code) + if len(data) != 0 { + t.Fatalf("Response Body len != 0") } - if resp.SQLInfo.AffectedCount != 1 { - t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo.AffectedCount) + if resp.SQLInfo().AffectedCount != 1 { + t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo().AffectedCount) } } @@ -2528,7 +2425,7 @@ func testConnectionDoSelectRequestPrepare(t *testing.T, conn Connector) { } func testConnectionDoSelectRequestCheck(t *testing.T, - resp *Response, err error, pos bool, dataLen int, firstKey uint64) { + resp Response, err error, pos bool, dataLen int, firstKey uint64) { t.Helper() if err != nil { @@ -2537,18 +2434,22 @@ func testConnectionDoSelectRequestCheck(t *testing.T, if resp == nil { t.Fatalf("Response is nil after Select") } - if !pos && resp.Pos != nil { + if !pos && resp.Pos() != nil { t.Errorf("Response should not have a position descriptor") } - if pos && resp.Pos == nil { + if pos && resp.Pos() == nil { t.Fatalf("A response must have a position descriptor") } - if len(resp.Data) != dataLen { - t.Fatalf("Response Data len %d != %d", len(resp.Data), dataLen) + data, err := resp.Decode() + if err != nil { + t.Fatalf("Failed to Decode: %s", err.Error()) + } + if len(data) != dataLen { + t.Fatalf("Response Data len %d != %d", len(data), dataLen) } for i := 0; i < dataLen; i++ { key := firstKey + uint64(i) - if tpl, ok := resp.Data[i].([]interface{}); !ok { + if tpl, ok := data[i].([]interface{}); !ok { t.Errorf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != key { @@ -2579,7 +2480,7 @@ func TestConnectionDoSelectRequest(t *testing.T) { Limit(20). Iterator(IterGe). Key([]interface{}{uint(1010)}) - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() testConnectionDoSelectRequestCheck(t, resp, err, false, 10, 1010) } @@ -2595,15 +2496,12 @@ func TestConnectionDoWatchOnceRequest(t *testing.T) { t.Fatalf("Failed to create a broadcast : %s", err.Error()) } - resp, err := conn.Do(NewWatchOnceRequest("hello")).Get() + data, err := conn.Do(NewWatchOnceRequest("hello")).Get() if err != nil { t.Fatalf("Failed to WatchOnce: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("Failed to WatchOnce: wrong code returned %d", resp.Code) - } - if len(resp.Data) < 1 || resp.Data[0] != "world" { - t.Errorf("Failed to WatchOnce: wrong value returned %v", resp.Data) + if len(data) < 1 || data[0] != "world" { + t.Errorf("Failed to WatchOnce: wrong value returned %v", data) } } @@ -2619,15 +2517,12 @@ func TestConnectionDoWatchOnceOnEmptyKey(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() - resp, err := conn.Do(NewWatchOnceRequest("notexists!")).Get() + data, err := conn.Do(NewWatchOnceRequest("notexists!")).Get() if err != nil { t.Fatalf("Failed to WatchOnce: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("Failed to WatchOnce: wrong code returned %d", resp.Code) - } - if len(resp.Data) > 0 { - t.Errorf("Failed to WatchOnce: wrong value returned %v", resp.Data) + if len(data) > 0 { + t.Errorf("Failed to WatchOnce: wrong value returned %v", data) } } @@ -2645,7 +2540,7 @@ func TestConnectionDoSelectRequest_fetch_pos(t *testing.T) { Iterator(IterGe). FetchPos(true). Key([]interface{}{uint(1010)}) - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1010) } @@ -2665,7 +2560,7 @@ func TestConnectDoSelectRequest_after_tuple(t *testing.T) { FetchPos(true). Key([]interface{}{uint(1010)}). After([]interface{}{uint(1012)}) - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1013) } @@ -2684,17 +2579,17 @@ func TestConnectionDoSelectRequest_pagination_pos(t *testing.T) { Iterator(IterGe). FetchPos(true). Key([]interface{}{uint(1010)}) - resp, err := conn.Do(req).Get() + resp, err := conn.Do(req).GetResponse() testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1010) - resp, err = conn.Do(req.After(resp.Pos)).Get() + resp, err = conn.Do(req.After(resp.Pos())).GetResponse() testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1012) } func TestConnection_Call(t *testing.T) { - var resp *Response + var resp Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) @@ -2704,25 +2599,28 @@ func TestConnection_Call(t *testing.T) { if err != nil { t.Errorf("Failed to use Call") } - if val, ok := resp.Data[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + data, err := resp.Decode() + if err != nil { + t.Errorf("Failed to Decode: %s", err.Error()) + } + if val, ok := data[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } } func TestCallRequest(t *testing.T) { - var resp *Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() req := NewCallRequest("simple_concat").Args([]interface{}{"1"}) - resp, err = conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Errorf("Failed to use Call") } - if val, ok := resp.Data[0].(string); !ok || val != "11" { - t.Errorf("result is not {{1}} : %v", resp.Data) + if val, ok := data[0].(string); !ok || val != "11" { + t.Errorf("result is not {{1}} : %v", data) } } @@ -2730,14 +2628,11 @@ func TestClientRequestObjectsWithNilContext(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() req := NewPingRequest().Context(nil) //nolint - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() if err != nil { t.Fatalf("Failed to Ping: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Ping") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Errorf("Response Body len != 0") } } @@ -2886,7 +2781,6 @@ func TestStream_IdValues(t *testing.T) { func TestStream_Commit(t *testing.T) { var req Request - var resp *Response var err error var conn *Connection @@ -2899,24 +2793,18 @@ func TestStream_Commit(t *testing.T) { // Begin transaction req = NewBeginRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Begin: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Begin: wrong code returned %d", resp.Code) - } // Insert in stream req = NewInsertRequest(spaceName). Tuple([]interface{}{uint(1001), "hello2", "world2"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) - } defer test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{uint(1001)}) // Select not related to the transaction @@ -2927,29 +2815,23 @@ func TestStream_Commit(t *testing.T) { Limit(1). Iterator(IterEq). Key([]interface{}{uint(1001)}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != 1001 { @@ -2965,26 +2847,20 @@ func TestStream_Commit(t *testing.T) { // Commit transaction req = NewCommitRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Commit: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Commit: wrong code returned %d", resp.Code) - } // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != 1001 { @@ -3001,7 +2877,6 @@ func TestStream_Commit(t *testing.T) { func TestStream_Rollback(t *testing.T) { var req Request - var resp *Response var err error var conn *Connection @@ -3014,24 +2889,18 @@ func TestStream_Rollback(t *testing.T) { // Begin transaction req = NewBeginRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Begin: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Begin: wrong code returned %d", resp.Code) - } // Insert in stream req = NewInsertRequest(spaceName). Tuple([]interface{}{uint(1001), "hello2", "world2"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp.Code != OkCode { - t.Errorf("Failed to Insert: wrong code returned %d", resp.Code) - } defer test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{uint(1001)}) // Select not related to the transaction @@ -3042,29 +2911,23 @@ func TestStream_Rollback(t *testing.T) { Limit(1). Iterator(IterEq). Key([]interface{}{uint(1001)}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 1 { + if len(data) != 1 { t.Fatalf("Response Data len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { t.Fatalf("Unexpected body of Select") } else { if id, err := test_helpers.ConvertUint64(tpl[0]); err != nil || id != 1001 { @@ -3080,42 +2943,32 @@ func TestStream_Rollback(t *testing.T) { // Rollback transaction req = NewRollbackRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() if err != nil { t.Fatalf("Failed to Rollback: %s", err.Error()) } - if resp.Code != OkCode { - t.Fatalf("Failed to Rollback: wrong code returned %d", resp.Code) - } // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } // Select inside of stream after rollback - resp, err = stream.Do(selectReq).Get() + _, err = stream.Do(selectReq).Get() if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if len(resp.Data) != 0 { + if len(data) != 0 { t.Fatalf("Response Data len != 0") } } func TestStream_TxnIsolationLevel(t *testing.T) { var req Request - var resp *Response var err error var conn *Connection @@ -3136,18 +2989,14 @@ func TestStream_TxnIsolationLevel(t *testing.T) { for _, level := range txnIsolationLevels { // Begin transaction req = NewBeginRequest().TxnIsolation(level).Timeout(500 * time.Millisecond) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Begin") - require.NotNilf(t, resp, "response is nil after Begin") - require.Equalf(t, OkCode, resp.Code, "wrong code returned") // Insert in stream req = NewInsertRequest(spaceName). Tuple([]interface{}{uint(1001), "hello2", "world2"}) - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - require.Equalf(t, OkCode, resp.Code, "wrong code returned") // Select not related to the transaction // while transaction is not committed @@ -3157,18 +3006,16 @@ func TestStream_TxnIsolationLevel(t *testing.T) { Limit(1). Iterator(IterEq). Key([]interface{}{uint(1001)}) - resp, err = conn.Do(selectReq).Get() + data, err := conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select in stream - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 1, len(resp.Data), "response Body len != 1 after Select") + require.Equalf(t, 1, len(data), "response Body len != 1 after Select") - tpl, ok := resp.Data[0].([]interface{}) + tpl, ok := data[0].([]interface{}) require.Truef(t, ok, "unexpected body of Select") require.Equalf(t, 3, len(tpl), "unexpected body of Select") @@ -3186,22 +3033,18 @@ func TestStream_TxnIsolationLevel(t *testing.T) { // Rollback transaction req = NewRollbackRequest() - resp, err = stream.Do(req).Get() + _, err = stream.Do(req).Get() require.Nilf(t, err, "failed to Rollback") - require.NotNilf(t, resp, "response is nil after Rollback") - require.Equalf(t, OkCode, resp.Code, "wrong code returned") // Select outside of transaction - resp, err = conn.Do(selectReq).Get() + data, err = conn.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") // Select inside of stream after rollback - resp, err = stream.Do(selectReq).Get() + data, err = stream.Do(selectReq).Get() require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - require.Equalf(t, 0, len(resp.Data), "response Data len != 0") + require.Equalf(t, 0, len(data), "response Data len != 0") test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{uint(1001)}) } @@ -3298,13 +3141,12 @@ func TestClientIdRequestObject(t *testing.T) { Version: ProtocolVersion(1), Features: []iproto.Feature{iproto.IPROTO_FEATURE_STREAMS}, }) - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() require.Nilf(t, err, "No errors on Id request execution") - require.NotNilf(t, resp, "Response not empty") - require.NotNilf(t, resp.Data, "Response data not empty") - require.Equal(t, len(resp.Data), 1, "Response data contains exactly one object") + require.NotNilf(t, data, "Response data not empty") + require.Equal(t, len(data), 1, "Response data contains exactly one object") - serverProtocolInfo, ok := resp.Data[0].(ProtocolInfo) + serverProtocolInfo, ok := data[0].(ProtocolInfo) require.Truef(t, ok, "Response Data object is an ProtocolInfo object") require.GreaterOrEqual(t, serverProtocolInfo.Version, @@ -3334,13 +3176,12 @@ func TestClientIdRequestObjectWithNilContext(t *testing.T) { Version: ProtocolVersion(1), Features: []iproto.Feature{iproto.IPROTO_FEATURE_STREAMS}, }).Context(nil) //nolint - resp, err := conn.Do(req).Get() + data, err := conn.Do(req).Get() require.Nilf(t, err, "No errors on Id request execution") - require.NotNilf(t, resp, "Response not empty") - require.NotNilf(t, resp.Data, "Response data not empty") - require.Equal(t, len(resp.Data), 1, "Response data contains exactly one object") + require.NotNilf(t, data, "Response data not empty") + require.Equal(t, len(data), 1, "Response data contains exactly one object") - serverProtocolInfo, ok := resp.Data[0].(ProtocolInfo) + serverProtocolInfo, ok := data[0].(ProtocolInfo) require.Truef(t, ok, "Response Data object is an ProtocolInfo object") require.GreaterOrEqual(t, serverProtocolInfo.Version, @@ -3697,15 +3538,12 @@ func TestBroadcastRequest(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() - resp, err := conn.Do(NewBroadcastRequest(key).Value(value)).Get() + data, err := conn.Do(NewBroadcastRequest(key).Value(value)).Get() if err != nil { t.Fatalf("Got broadcast error: %s", err) } - if resp.Code != OkCode { - t.Errorf("Got unexpected broadcast response code: %d", resp.Code) - } - if !reflect.DeepEqual(resp.Data, []interface{}{}) { - t.Errorf("Got unexpected broadcast response data: %v", resp.Data) + if !reflect.DeepEqual(data, []interface{}{}) { + t.Errorf("Got unexpected broadcast response data: %v", data) } events := make(chan WatchEvent) diff --git a/test_helpers/main.go b/test_helpers/main.go index d81ce3d1b..0a0c7f1cd 100644 --- a/test_helpers/main.go +++ b/test_helpers/main.go @@ -83,7 +83,7 @@ type TarantoolInstance struct { func isReady(dialer tarantool.Dialer, opts *tarantool.Opts) error { var err error var conn *tarantool.Connection - var resp *tarantool.Response + var resp tarantool.Response ctx, cancel := GetConnectContext() defer cancel() @@ -96,7 +96,7 @@ func isReady(dialer tarantool.Dialer, opts *tarantool.Opts) error { } defer conn.Close() - resp, err = conn.Do(tarantool.NewPingRequest()).Get() + resp, err = conn.Do(tarantool.NewPingRequest()).GetResponse() if err != nil { return err } diff --git a/test_helpers/pool_helper.go b/test_helpers/pool_helper.go index fb59418d5..729a69e44 100644 --- a/test_helpers/pool_helper.go +++ b/test_helpers/pool_helper.go @@ -84,18 +84,15 @@ func ProcessListenOnInstance(args interface{}) error { for i := 0; i < listenArgs.ServersNumber; i++ { req := tarantool.NewEvalRequest("return box.cfg.listen") - resp, err := listenArgs.ConnPool.Do(req, listenArgs.Mode).Get() + data, err := listenArgs.ConnPool.Do(req, listenArgs.Mode).Get() if err != nil { return fmt.Errorf("fail to Eval: %s", err.Error()) } - if resp == nil { - return fmt.Errorf("response is nil after Eval") - } - if len(resp.Data) < 1 { + if len(data) < 1 { return fmt.Errorf("response.Data is empty after Eval") } - port, ok := resp.Data[0].(string) + port, ok := data[0].(string) if !ok { return fmt.Errorf("response.Data is incorrect after Eval") } @@ -142,17 +139,14 @@ func InsertOnInstance(ctx context.Context, dialer tarantool.Dialer, connOpts tar } defer conn.Close() - resp, err := conn.Do(tarantool.NewInsertRequest(space).Tuple(tuple)).Get() + data, err := conn.Do(tarantool.NewInsertRequest(space).Tuple(tuple)).Get() if err != nil { return fmt.Errorf("failed to Insert: %s", err.Error()) } - if resp == nil { - return fmt.Errorf("response is nil after Insert") - } - if len(resp.Data) != 1 { + if len(data) != 1 { return fmt.Errorf("response Body len != 1") } - if tpl, ok := resp.Data[0].([]interface{}); !ok { + if tpl, ok := data[0].([]interface{}); !ok { return fmt.Errorf("unexpected body of Insert") } else { expectedTpl, ok := tuple.([]interface{}) diff --git a/uuid/example_test.go b/uuid/example_test.go index ba90ea905..c79dc35be 100644 --- a/uuid/example_test.go +++ b/uuid/example_test.go @@ -46,12 +46,11 @@ func Example() { log.Fatalf("Failed to prepare uuid: %s", uuidErr) } - resp, err := client.Do(tarantool.NewReplaceRequest(spaceNo). + data, err := client.Do(tarantool.NewReplaceRequest(spaceNo). Tuple([]interface{}{id}), ).Get() fmt.Println("UUID tuple replace") fmt.Println("Error", err) - fmt.Println("Code", resp.Code) - fmt.Println("Data", resp.Data) + fmt.Println("Data", data) } diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index fdbf0cd82..0ce317979 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -89,14 +89,11 @@ func TestSelect(t *testing.T) { Limit(1). Iterator(IterEq). Key([]interface{}{id}) - resp, errSel := conn.Do(sel).Get() + data, errSel := conn.Do(sel).Get() if errSel != nil { t.Fatalf("UUID select failed: %s", errSel.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - tupleValueIsId(t, resp.Data, id) + tupleValueIsId(t, data, id) var tuples []TupleUUID errTyp := conn.Do(sel).GetTyped(&tuples) @@ -125,28 +122,22 @@ func TestReplace(t *testing.T) { } rep := NewReplaceRequest(space).Tuple([]interface{}{id}) - respRep, errRep := conn.Do(rep).Get() + dataRep, errRep := conn.Do(rep).Get() if errRep != nil { t.Errorf("UUID replace failed: %s", errRep) } - if respRep == nil { - t.Fatalf("Response is nil after Replace") - } - tupleValueIsId(t, respRep.Data, id) + tupleValueIsId(t, dataRep, id) sel := NewSelectRequest(space). Index(index). Limit(1). Iterator(IterEq). Key([]interface{}{id}) - respSel, errSel := conn.Do(sel).Get() + dataSel, errSel := conn.Do(sel).Get() if errSel != nil { t.Errorf("UUID select failed: %s", errSel) } - if respSel == nil { - t.Fatalf("Response is nil after Select") - } - tupleValueIsId(t, respSel.Data, id) + tupleValueIsId(t, dataSel, id) } // runTestMain is a body of TestMain function From dc40391144f44f3967e5adf416aa1289b059148f Mon Sep 17 00:00:00 2001 From: DerekBum Date: Thu, 14 Dec 2023 18:35:00 +0300 Subject: [PATCH 2/4] api: create different responses for different requests Different implementations of the `Response` interface created. Special types of responses are used with special requests. Thus `Response` interface was simplified: some special methods were moved to the corresponding implementations. This means that if a user wants to access this methods, the response should be casted to its actual type. `SelectResponse`, `ExecuteResponse`, `PrepareResponse`, `PushResponse` are part of a public API. `Pos()`, `MetaData()`, `SQLInfo()` methods created for them to get specific info. `Future` constructors now accept `Request` as their argument. `Future` methods `AppendPush` and `SetResponse` accept response `Header` and data as their arguments. `IsPush()` method is used to return the information if the current response is a `PushResponse`. `PushCode` constant is removed. To get information, if the current response is a push response, `IsPush()` method could be used instead. After this patch, operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` return response data instead of an actual responses. After this patch, operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Pooler` return response data instead of an actual responses. Part of #237 --- CHANGELOG.md | 16 +- README.md | 18 ++ box_error_test.go | 9 +- connection.go | 33 +- connector.go | 24 +- crud/common.go | 7 + dial.go | 17 +- example_test.go | 101 +++++- future.go | 45 ++- future_test.go | 61 ++-- pool/connection_pool.go | 24 +- pool/connection_pool_test.go | 189 +++++------- pool/connector.go | 24 +- pool/connector_test.go | 62 ++-- pool/pooler.go | 24 +- prepared.go | 19 ++ request.go | 101 ++++-- response.go | 575 ++++++++++++++++++++++++++--------- settings/example_test.go | 28 +- settings/request.go | 13 + settings/tarantool_test.go | 114 +++++-- tarantool_test.go | 506 +++++++++++++++--------------- test_helpers/main.go | 6 +- test_helpers/request_mock.go | 8 + watch.go | 10 + 25 files changed, 1326 insertions(+), 708 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b362a4d9..e15812510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Support connection via an existing socket fd (#321) - `Header` struct for the response header (#237). It can be accessed via `Header()` method of the `Response` interface. +- `Response` method added to the `Request` interface (#237) +- New `LogAppendPushFailed` connection log constant (#237). + It is logged when connection fails to append a push response. ### Changed @@ -70,12 +73,21 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Rename `pool.GetPoolInfo` to `pool.GetInfo`. Change return type to `map[string]ConnectionInfo` (#321) - `Response` is now an interface (#237) -- All responses are now implementations of the `Response` interface (#237) +- All responses are now implementations of the `Response` interface (#237). + `SelectResponse`, `ExecuteResponse`, `PrepareResponse`, `PushResponse` are part + of a public API. `Pos()`, `MetaData()`, `SQLInfo()` methods created for them + to get specific info. + Special types of responses are used with special requests. - `IsPush()` method is added to the response iterator (#237). It returns the information if the current response is a `PushResponse`. `PushCode` constant is removed. - Method `Get` for `Future` now returns response data (#237). To get the actual - response new `GetResponse` method has been added. + response new `GetResponse` method has been added. Methods `AppendPush` and + `SetResponse` accept response `Header` and data as their arguments. +- `Future` constructors now accept `Request` as their argument (#237) +- Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, + `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` and `Pooler` + return response data instead of an actual responses (#237) ### Deprecated diff --git a/README.md b/README.md index a08ce1794..4f82896de 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,9 @@ The subpackage has been deleted. You could use `pool` instead. unique string ID, which allows them to be distinguished. * `pool.GetPoolInfo` has been renamed to `pool.GetInfo`. Return type has been changed to `map[string]ConnectionInfo`. +* Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, + `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Pooler` return + response data instead of an actual responses. #### crud package @@ -257,6 +260,7 @@ longer accept `ops` argument (operations) as an `interface{}`. `*Operations` needs to be passed instead. * `UpdateRequest` and `UpsertRequest` structs no longer accept `interface{}` for an `ops` field. `*Operations` needs to be used instead. +* `Response` method added to the `Request` interface. #### Response changes @@ -265,11 +269,25 @@ for an `ops` field. `*Operations` needs to be used instead. `Header()` method. * `ResponseIterator` interface now has `IsPush()` method. It returns true if the current response is a push response. +* For each request type, a different response type is created. They all + implement a `Response` interface. `SelectResponse`, `PrepareResponse`, + `ExecuteResponse`, `PushResponse` are a part of a public API. + `Pos()`, `MetaData()`, `SQLInfo()` methods created for them to get specific info. + Special types of responses are used with special requests. #### Future changes * Method `Get` now returns response data instead of the actual response. * New method `GetResponse` added to get an actual response. +* `Future` constructors now accept `Request` as their argument. +* Methods `AppendPush` and `SetResponse` accepts response `Header` and data + as their arguments. + +#### Connector changes + +Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, +`Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` return +response data instead of an actual responses. #### Connect function diff --git a/box_error_test.go b/box_error_test.go index 6839a1477..c48303bba 100644 --- a/box_error_test.go +++ b/box_error_test.go @@ -304,9 +304,7 @@ func TestErrorTypeEval(t *testing.T) { for name, testcase := range tupleCases { t.Run(name, func(t *testing.T) { - resp, err := conn.Eval("return ...", []interface{}{&testcase.tuple.val}) - require.Nil(t, err) - data, err := resp.Decode() + data, err := conn.Eval("return ...", []interface{}{&testcase.tuple.val}) require.Nil(t, err) require.NotNil(t, data) require.Equal(t, len(data), 1) @@ -438,14 +436,11 @@ func TestErrorTypeSelect(t *testing.T) { _, err := conn.Eval(insertEval, []interface{}{}) require.Nilf(t, err, "Tuple has been successfully inserted") - var resp Response var offset uint32 = 0 var limit uint32 = 1 - resp, err = conn.Select(space, index, offset, limit, IterEq, + data, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{testcase.tuple.pk}) require.Nil(t, err) - data, err := resp.Decode() - require.Nil(t, err) require.NotNil(t, data) require.Equalf(t, len(data), 1, "Exactly one tuple had been found") tpl, ok := data[0].([]interface{}) diff --git a/connection.go b/connection.go index 1dab74187..8e3939190 100644 --- a/connection.go +++ b/connection.go @@ -61,6 +61,8 @@ const ( LogUnexpectedResultId // LogWatchEventReadFailed is logged when failed to read a watch event. LogWatchEventReadFailed + // LogAppendPushFailed is logged when failed to append a push response. + LogAppendPushFailed ) // ConnEvent is sent throw Notify channel specified in Opts. @@ -103,6 +105,9 @@ func (d defaultLogger) Report(event ConnLogKind, conn *Connection, v ...interfac case LogWatchEventReadFailed: err := v[0].(error) log.Printf("tarantool: unable to parse watch event: %s", err) + case LogAppendPushFailed: + err := v[0].(error) + log.Printf("tarantool: unable to append a push response: %s", err) default: args := append([]interface{}{"tarantool: unexpected event ", event, conn}, v...) log.Print(args...) @@ -818,10 +823,9 @@ func (conn *Connection) reader(r io.Reader, c Conn) { return } - resp := &ConnResponse{header: header, buf: buf} var fut *Future = nil if iproto.Type(header.Code) == iproto.IPROTO_EVENT { - if event, err := readWatchEvent(&resp.buf); err == nil { + if event, err := readWatchEvent(&buf); err == nil { events <- event } else { err = ClientError{ @@ -833,11 +837,19 @@ func (conn *Connection) reader(r io.Reader, c Conn) { continue } else if header.Code == uint32(iproto.IPROTO_CHUNK) { if fut = conn.peekFuture(header.RequestId); fut != nil { - fut.AppendPush(resp) + if err := fut.AppendPush(header, &buf); err != nil { + err = ClientError{ + ErrProtocolError, + fmt.Sprintf("failed to append push response: %s", err), + } + conn.opts.Logger.Report(LogAppendPushFailed, conn, err) + } } } else { if fut = conn.fetchFuture(header.RequestId); fut != nil { - fut.SetResponse(resp) + if err := fut.SetResponse(header, &buf); err != nil { + fut.SetError(fmt.Errorf("failed to set response: %w", err)) + } conn.markDone(fut) } } @@ -873,8 +885,10 @@ func (conn *Connection) eventer(events <-chan connWatchEvent) { } } -func (conn *Connection) newFuture(ctx context.Context) (fut *Future) { +func (conn *Connection) newFuture(req Request) (fut *Future) { + ctx := req.Ctx() fut = NewFuture() + fut.SetRequest(req) if conn.rlimit != nil && conn.opts.RLimitAction == RLimitDrop { select { case conn.rlimit <- struct{}{}: @@ -984,7 +998,7 @@ func (conn *Connection) decrementRequestCnt() { func (conn *Connection) send(req Request, streamId uint64) *Future { conn.incrementRequestCnt() - fut := conn.newFuture(req.Ctx()) + fut := conn.newFuture(req) if fut.ready == nil { conn.decrementRequestCnt() return fut @@ -1053,8 +1067,11 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) { if req.Async() { if fut = conn.fetchFuture(reqid); fut != nil { - resp := &ConnResponse{} - fut.SetResponse(resp) + header := Header{ + RequestId: reqid, + Code: OkCode, + } + fut.SetResponse(header, nil) conn.markDone(fut) } } diff --git a/connector.go b/connector.go index b7f5affed..72b5d19a8 100644 --- a/connector.go +++ b/connector.go @@ -13,41 +13,41 @@ type Connector interface { // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. - Ping() (Response, error) + Ping() ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. Select(space, index interface{}, offset, limit uint32, iterator Iter, - key interface{}) (Response, error) + key interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. - Insert(space interface{}, tuple interface{}) (Response, error) + Insert(space interface{}, tuple interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a ReplicaRequest object + Do() instead. - Replace(space interface{}, tuple interface{}) (Response, error) + Replace(space interface{}, tuple interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. - Delete(space, index interface{}, key interface{}) (Response, error) + Delete(space, index interface{}, key interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. - Update(space, index interface{}, key interface{}, ops *Operations) (Response, error) + Update(space, index interface{}, key interface{}, ops *Operations) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. - Upsert(space interface{}, tuple interface{}, ops *Operations) (Response, error) + Upsert(space interface{}, tuple interface{}, ops *Operations) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. - Call(functionName string, args interface{}) (Response, error) + Call(functionName string, args interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. - Call16(functionName string, args interface{}) (Response, error) + Call16(functionName string, args interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. - Call17(functionName string, args interface{}) (Response, error) + Call17(functionName string, args interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. - Eval(expr string, args interface{}) (Response, error) + Eval(expr string, args interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. - Execute(expr string, args interface{}) (Response, error) + Execute(expr string, args interface{}) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. diff --git a/crud/common.go b/crud/common.go index 7a6f9e03b..061818487 100644 --- a/crud/common.go +++ b/crud/common.go @@ -55,6 +55,7 @@ package crud import ( "context" + "io" "github.com/tarantool/go-iproto" @@ -84,6 +85,12 @@ func (req baseRequest) Async() bool { return req.impl.Async() } +// Response creates a response for the baseRequest. +func (req baseRequest) Response(header tarantool.Header, + body io.Reader) (tarantool.Response, error) { + return req.impl.Response(header, body) +} + type spaceRequest struct { baseRequest space string diff --git a/dial.go b/dial.go index 37cbe0139..378687925 100644 --- a/dial.go +++ b/dial.go @@ -404,16 +404,11 @@ func identify(w writeFlusher, r io.Reader) (ProtocolInfo, error) { } data, err := resp.Decode() if err != nil { - switch err := err.(type) { - case Error: - if err.Code == iproto.ER_UNKNOWN_REQUEST_TYPE { - // IPROTO_ID requests are not supported by server. - return info, nil - } - return info, err - default: - return info, fmt.Errorf("decode response body error: %w", err) + if iproto.Error(resp.Header().Code) == iproto.ER_UNKNOWN_REQUEST_TYPE { + // IPROTO_ID requests are not supported by server. + return info, nil } + return info, err } if len(data) == 0 { @@ -511,12 +506,12 @@ func readResponse(r io.Reader) (Response, error) { respBytes, err := read(r, lenbuf[:]) if err != nil { - return &ConnResponse{}, fmt.Errorf("read error: %w", err) + return &BaseResponse{}, fmt.Errorf("read error: %w", err) } buf := smallBuf{b: respBytes} header, err := decodeHeader(msgpack.NewDecoder(&smallBuf{}), &buf) - resp := &ConnResponse{header: header, buf: buf} + resp := &BaseResponse{header: header, buf: buf} if err != nil { return resp, fmt.Errorf("decode response header error: %w", err) } diff --git a/example_test.go b/example_test.go index a39b9d5a1..3c4d12112 100644 --- a/example_test.go +++ b/example_test.go @@ -570,8 +570,17 @@ func ExampleExecuteRequest() { data, err := resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok := resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err := exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err := exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // There are 4 options to pass named parameters to an SQL query: // 1) The simple map; @@ -608,8 +617,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // 2) req = req.Args(sqlBind2) @@ -619,8 +637,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // 3) req = req.Args(sqlBind3) @@ -630,8 +657,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // 4) req = req.Args(sqlBind4) @@ -641,8 +677,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // The way to pass positional arguments to an SQL query. req = tarantool.NewExecuteRequest( @@ -654,8 +699,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) // The way to pass SQL expression with using custom packing/unpacking for // a type. @@ -680,8 +734,17 @@ func ExampleExecuteRequest() { data, err = resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) - fmt.Println("MetaData", resp.MetaData()) - fmt.Println("SQL Info", resp.SQLInfo()) + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + fmt.Println("MetaData", metaData) + fmt.Println("Error", err) + sqlInfo, err = exResp.SQLInfo() + fmt.Println("SQL Info", sqlInfo) + fmt.Println("Error", err) } func getTestTxnDialer() tarantool.Dialer { @@ -1158,6 +1221,15 @@ func ExampleConnection_Do_failure() { // and becomes available. We could wait for that moment with Future.Get(), // Future.GetResponse() or Future.GetTyped() methods. resp, err := future.GetResponse() + if err != nil { + fmt.Printf("Error in the future: %s\n", err) + } + + data, err := future.Get() + if err != nil { + fmt.Printf("Data: %v\n", data) + } + if err != nil { // We don't print the error here to keep the example reproducible. // fmt.Printf("Failed to execute the request: %s\n", err) @@ -1179,6 +1251,7 @@ func ExampleConnection_Do_failure() { } // Output: + // Data: [] // Error code from the response: 33 // Error code from the error: 33 // Error short from the error: ER_NO_SUCH_PROC diff --git a/future.go b/future.go index 7281b6149..139782637 100644 --- a/future.go +++ b/future.go @@ -1,6 +1,7 @@ package tarantool import ( + "io" "sync" "time" ) @@ -8,6 +9,7 @@ import ( // Future is a handle for asynchronous request. type Future struct { requestId uint32 + req Request next *Future timeout time.Duration mutex sync.Mutex @@ -117,6 +119,19 @@ func (it *asyncResponseIterator) nextResponse() (resp Response) { return resp } +// PushResponse is used for push requests for the Future. +type PushResponse struct { + BaseResponse +} + +func createPushResponse(header Header, body io.Reader) (Response, error) { + resp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &PushResponse{resp}, nil +} + // NewFuture creates a new empty Future. func NewFuture() (fut *Future) { fut = &Future{} @@ -131,30 +146,46 @@ func NewFuture() (fut *Future) { // // Deprecated: the method will be removed in the next major version, // use Connector.NewWatcher() instead of box.session.push(). -func (fut *Future) AppendPush(resp Response) { +func (fut *Future) AppendPush(header Header, body io.Reader) error { fut.mutex.Lock() defer fut.mutex.Unlock() if fut.isDone() { - return + return nil + } + resp, err := createPushResponse(header, body) + if err != nil { + return err } fut.pushes = append(fut.pushes, resp) fut.ready <- struct{}{} + return nil +} + +// SetRequest sets a request, for which the future was created. +func (fut *Future) SetRequest(req Request) { + fut.req = req } // SetResponse sets a response for the future and finishes the future. -func (fut *Future) SetResponse(resp Response) { +func (fut *Future) SetResponse(header Header, body io.Reader) error { fut.mutex.Lock() defer fut.mutex.Unlock() if fut.isDone() { - return + return nil + } + + resp, err := fut.req.Response(header, body) + if err != nil { + return err } fut.resp = resp close(fut.ready) close(fut.done) + return nil } // SetError sets an error for the future and finishes the future. @@ -179,11 +210,7 @@ func (fut *Future) SetError(err error) { // or ClientError, if something bad happens in a client process. func (fut *Future) GetResponse() (Response, error) { fut.wait() - if fut.err != nil { - return fut.resp, fut.err - } - _, err := fut.resp.Decode() - return fut.resp, err + return fut.resp, fut.err } // Get waits for Future to be filled and returns the data of the Response and error. diff --git a/future_test.go b/future_test.go index 3a3d8d01d..947e4ed28 100644 --- a/future_test.go +++ b/future_test.go @@ -54,9 +54,10 @@ func TestFutureGetIteratorNoItems(t *testing.T) { } func TestFutureGetIteratorNoResponse(t *testing.T) { - push := &ConnResponse{} + pushHeader := Header{} + push := &PushResponse{} fut := NewFuture() - fut.AppendPush(push) + fut.AppendPush(pushHeader, nil) if it := fut.GetIterator(); it.Next() { assertResponseIteratorValue(t, it, true, push) @@ -70,9 +71,10 @@ func TestFutureGetIteratorNoResponse(t *testing.T) { } func TestFutureGetIteratorNoResponseTimeout(t *testing.T) { - push := &ConnResponse{} + pushHeader := Header{} + push := &PushResponse{} fut := NewFuture() - fut.AppendPush(push) + fut.AppendPush(pushHeader, nil) if it := fut.GetIterator().WithTimeout(1 * time.Nanosecond); it.Next() { assertResponseIteratorValue(t, it, true, push) @@ -86,10 +88,12 @@ func TestFutureGetIteratorNoResponseTimeout(t *testing.T) { } func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { - push := &ConnResponse{} - resp := &ConnResponse{} + pushHeader := Header{} + respHeader := Header{} + push := &PushResponse{} + resp := &BaseResponse{} fut := NewFuture() - fut.AppendPush(push) + fut.AppendPush(pushHeader, nil) var done sync.WaitGroup var wait sync.WaitGroup @@ -123,19 +127,21 @@ func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { }() wait.Wait() - fut.SetResponse(resp) + + fut.SetRequest(&InsertRequest{}) + fut.SetResponse(respHeader, nil) done.Wait() } func TestFutureGetIteratorFirstResponse(t *testing.T) { - resp1 := &ConnResponse{} - resp2 := &ConnResponse{} + resp := &BaseResponse{} fut := NewFuture() - fut.SetResponse(resp1) - fut.SetResponse(resp2) + fut.SetRequest(&InsertRequest{}) + fut.SetResponse(Header{}, nil) + fut.SetResponse(Header{}, nil) if it := fut.GetIterator(); it.Next() { - assertResponseIteratorValue(t, it, false, resp1) + assertResponseIteratorValue(t, it, false, resp) if it.Next() == true { t.Errorf("An unexpected next value.") } @@ -164,17 +170,19 @@ func TestFutureGetIteratorFirstError(t *testing.T) { } func TestFutureGetIteratorResponse(t *testing.T) { - responses := []*ConnResponse{ - {}, - {}, - {}, + responses := []Response{ + &PushResponse{}, + &PushResponse{}, + &BaseResponse{}, } + header := Header{} fut := NewFuture() - for i, resp := range responses { + fut.SetRequest(&InsertRequest{}) + for i := range responses { if i == len(responses)-1 { - fut.SetResponse(resp) + fut.SetResponse(header, nil) } else { - fut.AppendPush(resp) + fut.AppendPush(header, nil) } } @@ -202,14 +210,14 @@ func TestFutureGetIteratorResponse(t *testing.T) { func TestFutureGetIteratorError(t *testing.T) { const errMsg = "error message" - responses := []*ConnResponse{ + responses := []*PushResponse{ {}, {}, } err := errors.New(errMsg) fut := NewFuture() - for _, resp := range responses { - fut.AppendPush(resp) + for range responses { + fut.AppendPush(Header{}, nil) } fut.SetError(err) @@ -239,19 +247,18 @@ func TestFutureGetIteratorError(t *testing.T) { func TestFutureSetStateRaceCondition(t *testing.T) { err := errors.New("any error") - resp := &ConnResponse{} for i := 0; i < 1000; i++ { fut := NewFuture() + fut.SetRequest(&InsertRequest{}) for j := 0; j < 9; j++ { go func(opt int) { if opt%3 == 0 { - respAppend := &ConnResponse{} - fut.AppendPush(respAppend) + fut.AppendPush(Header{}, nil) } else if opt%3 == 1 { fut.SetError(err) } else { - fut.SetResponse(resp) + fut.SetResponse(Header{}, nil) } }(j) } diff --git a/pool/connection_pool.go b/pool/connection_pool.go index 747de45f4..3734c4c0a 100644 --- a/pool/connection_pool.go +++ b/pool/connection_pool.go @@ -373,7 +373,7 @@ func (p *ConnectionPool) GetInfo() map[string]ConnectionInfo { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (p *ConnectionPool) Ping(userMode Mode) (tarantool.Response, error) { +func (p *ConnectionPool) Ping(userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -388,7 +388,7 @@ func (p *ConnectionPool) Ping(userMode Mode) (tarantool.Response, error) { // use a SelectRequest object + Do() instead. func (p *ConnectionPool) Select(space, index interface{}, offset, limit uint32, - iterator tarantool.Iter, key interface{}, userMode ...Mode) (tarantool.Response, error) { + iterator tarantool.Iter, key interface{}, userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(ANY, userMode) if err != nil { return nil, err @@ -403,7 +403,7 @@ func (p *ConnectionPool) Select(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. func (p *ConnectionPool) Insert(space interface{}, tuple interface{}, - userMode ...Mode) (tarantool.Response, error) { + userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -418,7 +418,7 @@ func (p *ConnectionPool) Insert(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. func (p *ConnectionPool) Replace(space interface{}, tuple interface{}, - userMode ...Mode) (tarantool.Response, error) { + userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -433,7 +433,7 @@ func (p *ConnectionPool) Replace(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. func (p *ConnectionPool) Delete(space, index interface{}, key interface{}, - userMode ...Mode) (tarantool.Response, error) { + userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -448,7 +448,7 @@ func (p *ConnectionPool) Delete(space, index interface{}, key interface{}, // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. func (p *ConnectionPool) Update(space, index interface{}, key interface{}, - ops *tarantool.Operations, userMode ...Mode) (tarantool.Response, error) { + ops *tarantool.Operations, userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -463,7 +463,7 @@ func (p *ConnectionPool) Update(space, index interface{}, key interface{}, // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. func (p *ConnectionPool) Upsert(space interface{}, tuple interface{}, - ops *tarantool.Operations, userMode ...Mode) (tarantool.Response, error) { + ops *tarantool.Operations, userMode ...Mode) ([]interface{}, error) { conn, err := p.getConnByMode(RW, userMode) if err != nil { return nil, err @@ -478,7 +478,7 @@ func (p *ConnectionPool) Upsert(space interface{}, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. func (p *ConnectionPool) Call(functionName string, args interface{}, - userMode Mode) (tarantool.Response, error) { + userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -494,7 +494,7 @@ func (p *ConnectionPool) Call(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. func (p *ConnectionPool) Call16(functionName string, args interface{}, - userMode Mode) (tarantool.Response, error) { + userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -509,7 +509,7 @@ func (p *ConnectionPool) Call16(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. func (p *ConnectionPool) Call17(functionName string, args interface{}, - userMode Mode) (tarantool.Response, error) { + userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -523,7 +523,7 @@ func (p *ConnectionPool) Call17(functionName string, args interface{}, // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. func (p *ConnectionPool) Eval(expr string, args interface{}, - userMode Mode) (tarantool.Response, error) { + userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err @@ -537,7 +537,7 @@ func (p *ConnectionPool) Eval(expr string, args interface{}, // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. func (p *ConnectionPool) Execute(expr string, args interface{}, - userMode Mode) (tarantool.Response, error) { + userMode Mode) ([]interface{}, error) { conn, err := p.getNextConnection(userMode) if err != nil { return nil, err diff --git a/pool/connection_pool_test.go b/pool/connection_pool_test.go index 50096cc4c..ae1acd223 100644 --- a/pool/connection_pool_test.go +++ b/pool/connection_pool_test.go @@ -1140,11 +1140,9 @@ func TestCall(t *testing.T) { defer connPool.Close() // PreferRO - resp, err := connPool.Call("box.info", []interface{}{}, pool.PreferRO) + data, err := connPool.Call("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val := data[0].(map[interface{}]interface{})["ro"] @@ -1153,11 +1151,9 @@ func TestCall(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `PreferRO`") // PreferRW - resp, err = connPool.Call("box.info", []interface{}{}, pool.PreferRW) + data, err = connPool.Call("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1166,11 +1162,9 @@ func TestCall(t *testing.T) { require.Falsef(t, ro, "expected `false` with mode `PreferRW`") // RO - resp, err = connPool.Call("box.info", []interface{}{}, pool.RO) + data, err = connPool.Call("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1179,11 +1173,9 @@ func TestCall(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `RO`") // RW - resp, err = connPool.Call("box.info", []interface{}{}, pool.RW) + data, err = connPool.Call("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1207,11 +1199,9 @@ func TestCall16(t *testing.T) { defer connPool.Close() // PreferRO - resp, err := connPool.Call16("box.info", []interface{}{}, pool.PreferRO) + data, err := connPool.Call16("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val := data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] @@ -1220,11 +1210,9 @@ func TestCall16(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `PreferRO`") // PreferRW - resp, err = connPool.Call16("box.info", []interface{}{}, pool.PreferRW) + data, err = connPool.Call16("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] @@ -1233,11 +1221,9 @@ func TestCall16(t *testing.T) { require.Falsef(t, ro, "expected `false` with mode `PreferRW`") // RO - resp, err = connPool.Call16("box.info", []interface{}{}, pool.RO) + data, err = connPool.Call16("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] @@ -1246,11 +1232,9 @@ func TestCall16(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `RO`") // RW - resp, err = connPool.Call16("box.info", []interface{}{}, pool.RW) + data, err = connPool.Call16("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].([]interface{})[0].(map[interface{}]interface{})["ro"] @@ -1274,11 +1258,9 @@ func TestCall17(t *testing.T) { defer connPool.Close() // PreferRO - resp, err := connPool.Call17("box.info", []interface{}{}, pool.PreferRO) + data, err := connPool.Call17("box.info", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val := data[0].(map[interface{}]interface{})["ro"] @@ -1287,11 +1269,9 @@ func TestCall17(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `PreferRO`") // PreferRW - resp, err = connPool.Call17("box.info", []interface{}{}, pool.PreferRW) + data, err = connPool.Call17("box.info", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1300,11 +1280,9 @@ func TestCall17(t *testing.T) { require.Falsef(t, ro, "expected `false` with mode `PreferRW`") // RO - resp, err = connPool.Call17("box.info", []interface{}{}, pool.RO) + data, err = connPool.Call17("box.info", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1313,11 +1291,9 @@ func TestCall17(t *testing.T) { require.Truef(t, ro, "expected `true` with mode `RO`") // RW - resp, err = connPool.Call17("box.info", []interface{}{}, pool.RW) + data, err = connPool.Call17("box.info", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Call") - require.NotNilf(t, resp, "response is nil after Call") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Call") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Call") val = data[0].(map[interface{}]interface{})["ro"] @@ -1341,11 +1317,9 @@ func TestEval(t *testing.T) { defer connPool.Close() // PreferRO - resp, err := connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRO) + data, err := connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRO) require.Nilf(t, err, "failed to Eval") - require.NotNilf(t, resp, "response is nil after Eval") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Eval") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") val, ok := data[0].(bool) @@ -1353,11 +1327,9 @@ func TestEval(t *testing.T) { require.Truef(t, val, "expected `true` with mode `PreferRO`") // PreferRW - resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRW) + data, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.PreferRW) require.Nilf(t, err, "failed to Eval") - require.NotNilf(t, resp, "response is nil after Eval") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Eval") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") val, ok = data[0].(bool) @@ -1365,11 +1337,9 @@ func TestEval(t *testing.T) { require.Falsef(t, val, "expected `false` with mode `PreferRW`") // RO - resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RO) + data, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RO) require.Nilf(t, err, "failed to Eval") - require.NotNilf(t, resp, "response is nil after Eval") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Eval") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") val, ok = data[0].(bool) @@ -1377,11 +1347,9 @@ func TestEval(t *testing.T) { require.Truef(t, val, "expected `true` with mode `RO`") // RW - resp, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RW) + data, err = connPool.Eval("return box.info().ro", []interface{}{}, pool.RW) require.Nilf(t, err, "failed to Eval") - require.NotNilf(t, resp, "response is nil after Eval") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Eval") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Eval") val, ok = data[0].(bool) @@ -1430,11 +1398,9 @@ func TestExecute(t *testing.T) { request := "SELECT NAME0, NAME1 FROM SQL_TEST WHERE NAME0 == 1;" // Execute - resp, err := connPool.Execute(request, []interface{}{}, pool.ANY) + data, err := connPool.Execute(request, []interface{}{}, pool.ANY) require.Nilf(t, err, "failed to Execute") - require.NotNilf(t, resp, "response is nil after Execute") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Execute") require.GreaterOrEqualf(t, len(data), 1, "response.Data is empty after Execute") require.Equalf(t, len(data[0].([]interface{})), 2, "unexpected response") @@ -1872,11 +1838,9 @@ func TestInsert(t *testing.T) { defer connPool.Close() // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err := connPool.Insert(spaceName, []interface{}{"rw_insert_key", "rw_insert_value"}) + data, err := connPool.Insert(spaceName, []interface{}{"rw_insert_key", "rw_insert_value"}) require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Insert") require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") tpl, ok := data[0].([]interface{}) @@ -1918,12 +1882,10 @@ func TestInsert(t *testing.T) { require.Equalf(t, "rw_insert_value", value, "unexpected body of Select (1)") // PreferRW - resp, err = connPool.Insert(spaceName, + data, err = connPool.Insert(spaceName, []interface{}{"preferRW_insert_key", "preferRW_insert_value"}) require.Nilf(t, err, "failed to Insert") - require.NotNilf(t, resp, "response is nil after Insert") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Insert") require.Equalf(t, len(data), 1, "response Body len != 1 after Insert") tpl, ok = data[0].([]interface{}) @@ -1997,11 +1959,9 @@ func TestDelete(t *testing.T) { require.Equalf(t, "delete_value", value, "unexpected body of Insert (1)") // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err := connPool.Delete(spaceName, indexNo, []interface{}{"delete_key"}) + data, err = connPool.Delete(spaceName, indexNo, []interface{}{"delete_key"}) require.Nilf(t, err, "failed to Delete") - require.NotNilf(t, resp, "response is nil after Delete") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Delete") require.Equalf(t, len(data), 1, "response Body len != 1 after Delete") tpl, ok = data[0].([]interface{}) @@ -2046,18 +2006,18 @@ func TestUpsert(t *testing.T) { defer conn.Close() // Mode is `RW` by default, we have only one RW instance (servers[2]) - resp, err := connPool.Upsert(spaceName, + data, err := connPool.Upsert(spaceName, []interface{}{"upsert_key", "upsert_value"}, tarantool.NewOperations().Assign(1, "new_value")) require.Nilf(t, err, "failed to Upsert") - require.NotNilf(t, resp, "response is nil after Upsert") + require.NotNilf(t, data, "response is nil after Upsert") sel := tarantool.NewSelectRequest(spaceNo). Index(indexNo). Limit(1). Iterator(tarantool.IterEq). Key([]interface{}{"upsert_key"}) - data, err := conn.Do(sel).Get() + data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") @@ -2074,12 +2034,12 @@ func TestUpsert(t *testing.T) { require.Equalf(t, "upsert_value", value, "unexpected body of Select (1)") // PreferRW - resp, err = connPool.Upsert( + data, err = connPool.Upsert( spaceName, []interface{}{"upsert_key", "upsert_value"}, tarantool.NewOperations().Assign(1, "new_value"), pool.PreferRW) require.Nilf(t, err, "failed to Upsert") - require.NotNilf(t, resp, "response is nil after Upsert") + require.NotNilf(t, data, "response is nil after Upsert") data, err = conn.Do(sel).Get() require.Nilf(t, err, "failed to Select") @@ -2308,11 +2268,9 @@ func TestSelect(t *testing.T) { require.Nil(t, err) //default: ANY - resp, err := connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, anyKey) + data, err := connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, anyKey) require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - data, err := resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") tpl, ok := data[0].([]interface{}) @@ -2328,11 +2286,9 @@ func TestSelect(t *testing.T) { require.Equalf(t, "any_select_value", value, "unexpected body of Select (1)") // PreferRO - resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.PreferRO) + data, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.PreferRO) require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") tpl, ok = data[0].([]interface{}) @@ -2344,11 +2300,9 @@ func TestSelect(t *testing.T) { require.Equalf(t, "ro_select_key", key, "unexpected body of Select (0)") // PreferRW - resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.PreferRW) + data, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.PreferRW) require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") tpl, ok = data[0].([]interface{}) @@ -2364,11 +2318,9 @@ func TestSelect(t *testing.T) { require.Equalf(t, "rw_select_value", value, "unexpected body of Select (1)") // RO - resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.RO) + data, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, roKey, pool.RO) require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") tpl, ok = data[0].([]interface{}) @@ -2384,11 +2336,9 @@ func TestSelect(t *testing.T) { require.Equalf(t, "ro_select_value", value, "unexpected body of Select (1)") // RW - resp, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.RW) + data, err = connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, rwKey, pool.RW) require.Nilf(t, err, "failed to Select") - require.NotNilf(t, resp, "response is nil after Select") - data, err = resp.Decode() - require.Nilf(t, err, "failed to Decode") + require.NotNilf(t, data, "response is nil after Select") require.Equalf(t, len(data), 1, "response Body len != 1 after Select") tpl, ok = data[0].([]interface{}) @@ -2419,29 +2369,29 @@ func TestPing(t *testing.T) { defer connPool.Close() // ANY - resp, err := connPool.Ping(pool.ANY) + data, err := connPool.Ping(pool.ANY) require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") + require.Nilf(t, data, "response data is not nil after Ping") // RW - resp, err = connPool.Ping(pool.RW) + data, err = connPool.Ping(pool.RW) require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") + require.Nilf(t, data, "response data is not nil after Ping") // RO - resp, err = connPool.Ping(pool.RO) + data, err = connPool.Ping(pool.RO) require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") + require.Nilf(t, data, "response data is not nil after Ping") // PreferRW - resp, err = connPool.Ping(pool.PreferRW) + data, err = connPool.Ping(pool.PreferRW) require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") + require.Nilf(t, data, "response data is not nil after Ping") // PreferRO - resp, err = connPool.Ping(pool.PreferRO) + data, err = connPool.Ping(pool.PreferRO) require.Nilf(t, err, "failed to Ping") - require.NotNilf(t, resp, "response is nil after Ping") + require.Nilf(t, data, "response data is not nil after Ping") } func TestDo(t *testing.T) { @@ -2548,7 +2498,14 @@ func TestNewPrepared(t *testing.T) { if reflect.DeepEqual(data[0], []interface{}{1, "test"}) { t.Error("Select with named arguments failed") } - metaData := resp.MetaData() + prepResp, ok := resp.(*tarantool.ExecuteResponse) + if !ok { + t.Fatalf("Not a Prepare response") + } + metaData, err := prepResp.MetaData() + if err != nil { + t.Errorf("Error while getting MetaData: %s", err.Error()) + } if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || diff --git a/pool/connector.go b/pool/connector.go index 74a60bd74..23cc7275d 100644 --- a/pool/connector.go +++ b/pool/connector.go @@ -60,7 +60,7 @@ func (c *ConnectorAdapter) ConfiguredTimeout() time.Duration { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (c *ConnectorAdapter) Ping() (tarantool.Response, error) { +func (c *ConnectorAdapter) Ping() ([]interface{}, error) { return c.pool.Ping(c.mode) } @@ -70,7 +70,7 @@ func (c *ConnectorAdapter) Ping() (tarantool.Response, error) { // use a SelectRequest object + Do() instead. func (c *ConnectorAdapter) Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, - key interface{}) (tarantool.Response, error) { + key interface{}) ([]interface{}, error) { return c.pool.Select(space, index, offset, limit, iterator, key, c.mode) } @@ -79,7 +79,7 @@ func (c *ConnectorAdapter) Select(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. func (c *ConnectorAdapter) Insert(space interface{}, - tuple interface{}) (tarantool.Response, error) { + tuple interface{}) ([]interface{}, error) { return c.pool.Insert(space, tuple, c.mode) } @@ -88,7 +88,7 @@ func (c *ConnectorAdapter) Insert(space interface{}, // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. func (c *ConnectorAdapter) Replace(space interface{}, - tuple interface{}) (tarantool.Response, error) { + tuple interface{}) ([]interface{}, error) { return c.pool.Replace(space, tuple, c.mode) } @@ -97,7 +97,7 @@ func (c *ConnectorAdapter) Replace(space interface{}, // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. func (c *ConnectorAdapter) Delete(space, index interface{}, - key interface{}) (tarantool.Response, error) { + key interface{}) ([]interface{}, error) { return c.pool.Delete(space, index, key, c.mode) } @@ -106,7 +106,7 @@ func (c *ConnectorAdapter) Delete(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. func (c *ConnectorAdapter) Update(space, index interface{}, - key interface{}, ops *tarantool.Operations) (tarantool.Response, error) { + key interface{}, ops *tarantool.Operations) ([]interface{}, error) { return c.pool.Update(space, index, key, ops, c.mode) } @@ -115,7 +115,7 @@ func (c *ConnectorAdapter) Update(space, index interface{}, // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. func (c *ConnectorAdapter) Upsert(space, tuple interface{}, - ops *tarantool.Operations) (tarantool.Response, error) { + ops *tarantool.Operations) ([]interface{}, error) { return c.pool.Upsert(space, tuple, ops, c.mode) } @@ -125,7 +125,7 @@ func (c *ConnectorAdapter) Upsert(space, tuple interface{}, // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. func (c *ConnectorAdapter) Call(functionName string, - args interface{}) (tarantool.Response, error) { + args interface{}) ([]interface{}, error) { return c.pool.Call(functionName, args, c.mode) } @@ -136,7 +136,7 @@ func (c *ConnectorAdapter) Call(functionName string, // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. func (c *ConnectorAdapter) Call16(functionName string, - args interface{}) (tarantool.Response, error) { + args interface{}) ([]interface{}, error) { return c.pool.Call16(functionName, args, c.mode) } @@ -146,7 +146,7 @@ func (c *ConnectorAdapter) Call16(functionName string, // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. func (c *ConnectorAdapter) Call17(functionName string, - args interface{}) (tarantool.Response, error) { + args interface{}) ([]interface{}, error) { return c.pool.Call17(functionName, args, c.mode) } @@ -155,7 +155,7 @@ func (c *ConnectorAdapter) Call17(functionName string, // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. func (c *ConnectorAdapter) Eval(expr string, - args interface{}) (tarantool.Response, error) { + args interface{}) ([]interface{}, error) { return c.pool.Eval(expr, args, c.mode) } @@ -164,7 +164,7 @@ func (c *ConnectorAdapter) Eval(expr string, // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. func (c *ConnectorAdapter) Execute(expr string, - args interface{}) (tarantool.Response, error) { + args interface{}) ([]interface{}, error) { return c.pool.Execute(expr, args, c.mode) } diff --git a/pool/connector_test.go b/pool/connector_test.go index 190d2f9cc..87bebbd53 100644 --- a/pool/connector_test.go +++ b/pool/connector_test.go @@ -135,7 +135,7 @@ type baseRequestMock struct { mode Mode } -var reqResp tarantool.Response = &tarantool.ConnResponse{} +var reqData []interface{} var errReq error = errors.New("response error") var reqFuture *tarantool.Future = &tarantool.Future{} @@ -190,7 +190,7 @@ type selectMock struct { func (m *selectMock) Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, key interface{}, - mode ...Mode) (tarantool.Response, error) { + mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.index = index @@ -199,7 +199,7 @@ func (m *selectMock) Select(space, index interface{}, m.iterator = iterator m.key = key m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorSelect(t *testing.T) { @@ -208,7 +208,7 @@ func TestConnectorSelect(t *testing.T) { resp, err := c.Select(reqSpace, reqIndex, reqOffset, reqLimit, reqIterator, reqKey) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -299,12 +299,12 @@ type insertMock struct { } func (m *insertMock) Insert(space, tuple interface{}, - mode ...Mode) (tarantool.Response, error) { + mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.tuple = tuple m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorInsert(t *testing.T) { @@ -313,7 +313,7 @@ func TestConnectorInsert(t *testing.T) { resp, err := c.Insert(reqSpace, reqTuple) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -380,12 +380,12 @@ type replaceMock struct { } func (m *replaceMock) Replace(space, tuple interface{}, - mode ...Mode) (tarantool.Response, error) { + mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.tuple = tuple m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorReplace(t *testing.T) { @@ -394,7 +394,7 @@ func TestConnectorReplace(t *testing.T) { resp, err := c.Replace(reqSpace, reqTuple) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -461,13 +461,13 @@ type deleteMock struct { } func (m *deleteMock) Delete(space, index, key interface{}, - mode ...Mode) (tarantool.Response, error) { + mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.index = index m.key = key m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorDelete(t *testing.T) { @@ -476,7 +476,7 @@ func TestConnectorDelete(t *testing.T) { resp, err := c.Delete(reqSpace, reqIndex, reqKey) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -548,14 +548,14 @@ type updateMock struct { } func (m *updateMock) Update(space, index, key interface{}, - ops *tarantool.Operations, mode ...Mode) (tarantool.Response, error) { + ops *tarantool.Operations, mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.index = index m.key = key m.ops = ops m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorUpdate(t *testing.T) { @@ -564,7 +564,7 @@ func TestConnectorUpdate(t *testing.T) { resp, err := c.Update(reqSpace, reqIndex, reqKey, reqOps) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -641,13 +641,13 @@ type upsertMock struct { } func (m *upsertMock) Upsert(space, tuple interface{}, ops *tarantool.Operations, - mode ...Mode) (tarantool.Response, error) { + mode ...Mode) ([]interface{}, error) { m.called++ m.space = space m.tuple = tuple m.ops = ops m.mode = mode[0] - return reqResp, errReq + return reqData, errReq } func TestConnectorUpsert(t *testing.T) { @@ -656,7 +656,7 @@ func TestConnectorUpsert(t *testing.T) { resp, err := c.Upsert(reqSpace, reqTuple, reqOps) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqSpace, m.space, "unexpected space was passed") @@ -698,12 +698,12 @@ type baseCallMock struct { } func (m *baseCallMock) call(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { m.called++ m.functionName = functionName m.args = args m.mode = mode - return reqResp, errReq + return reqData, errReq } func (m *baseCallMock) callTyped(functionName string, args interface{}, @@ -730,7 +730,7 @@ type callMock struct { } func (m *callMock) Call(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { return m.call(functionName, args, mode) } @@ -740,7 +740,7 @@ func TestConnectorCall(t *testing.T) { resp, err := c.Call(reqFunctionName, reqArgs) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqFunctionName, m.functionName, @@ -801,7 +801,7 @@ type call16Mock struct { } func (m *call16Mock) Call16(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { return m.call(functionName, args, mode) } @@ -811,7 +811,7 @@ func TestConnectorCall16(t *testing.T) { resp, err := c.Call16(reqFunctionName, reqArgs) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqFunctionName, m.functionName, @@ -872,7 +872,7 @@ type call17Mock struct { } func (m *call17Mock) Call17(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { return m.call(functionName, args, mode) } @@ -882,7 +882,7 @@ func TestConnectorCall17(t *testing.T) { resp, err := c.Call17(reqFunctionName, reqArgs) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqFunctionName, m.functionName, @@ -943,7 +943,7 @@ type evalMock struct { } func (m *evalMock) Eval(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { return m.call(functionName, args, mode) } @@ -953,7 +953,7 @@ func TestConnectorEval(t *testing.T) { resp, err := c.Eval(reqFunctionName, reqArgs) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqFunctionName, m.functionName, @@ -1014,7 +1014,7 @@ type executeMock struct { } func (m *executeMock) Execute(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) { + mode Mode) ([]interface{}, error) { return m.call(functionName, args, mode) } @@ -1024,7 +1024,7 @@ func TestConnectorExecute(t *testing.T) { resp, err := c.Execute(reqFunctionName, reqArgs) - require.Equalf(t, reqResp, resp, "unexpected response") + require.Equalf(t, reqData, resp, "unexpected response") require.Equalf(t, errReq, err, "unexpected error") require.Equalf(t, 1, m.called, "should be called only once") require.Equalf(t, reqFunctionName, m.functionName, diff --git a/pool/pooler.go b/pool/pooler.go index 6256c2d24..975162d37 100644 --- a/pool/pooler.go +++ b/pool/pooler.go @@ -19,51 +19,51 @@ type Pooler interface { // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. - Ping(mode Mode) (tarantool.Response, error) + Ping(mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. Select(space, index interface{}, offset, limit uint32, iterator tarantool.Iter, - key interface{}, mode ...Mode) (tarantool.Response, error) + key interface{}, mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. Insert(space interface{}, tuple interface{}, - mode ...Mode) (tarantool.Response, error) + mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. Replace(space interface{}, tuple interface{}, - mode ...Mode) (tarantool.Response, error) + mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. Delete(space, index interface{}, key interface{}, - mode ...Mode) (tarantool.Response, error) + mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. Update(space, index interface{}, key interface{}, ops *tarantool.Operations, - mode ...Mode) (tarantool.Response, error) + mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. Upsert(space interface{}, tuple interface{}, ops *tarantool.Operations, - mode ...Mode) (tarantool.Response, error) + mode ...Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. Call(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) + mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. Call16(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) + mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. Call17(functionName string, args interface{}, - mode Mode) (tarantool.Response, error) + mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. Eval(expr string, args interface{}, - mode Mode) (tarantool.Response, error) + mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. Execute(expr string, args interface{}, - mode Mode) (tarantool.Response, error) + mode Mode) ([]interface{}, error) // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. diff --git a/prepared.go b/prepared.go index f4fc1cdf1..35b73272e 100644 --- a/prepared.go +++ b/prepared.go @@ -3,6 +3,7 @@ package tarantool import ( "context" "fmt" + "io" "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" @@ -95,6 +96,15 @@ func (req *PrepareRequest) Context(ctx context.Context) *PrepareRequest { return req } +// Response creates a response for the PrepareRequest. +func (req *PrepareRequest) Response(header Header, body io.Reader) (Response, error) { + baseResp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &PrepareResponse{BaseResponse: baseResp}, nil +} + // UnprepareRequest helps you to create an unprepare request object for // execution by a Connection. type UnprepareRequest struct { @@ -175,3 +185,12 @@ func (req *ExecutePreparedRequest) Context(ctx context.Context) *ExecutePrepared req.ctx = ctx return req } + +// Response creates a response for the ExecutePreparedRequest. +func (req *ExecutePreparedRequest) Response(header Header, body io.Reader) (Response, error) { + baseResp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &ExecuteResponse{BaseResponse: baseResp}, nil +} diff --git a/request.go b/request.go index bdf1c50ea..716e5ab2c 100644 --- a/request.go +++ b/request.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io" "reflect" "strings" "sync" @@ -260,8 +261,8 @@ func fillWatchOnce(enc *msgpack.Encoder, key string) error { // // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. -func (conn *Connection) Ping() (Response, error) { - return conn.Do(NewPingRequest()).GetResponse() +func (conn *Connection) Ping() ([]interface{}, error) { + return conn.Do(NewPingRequest()).Get() } // Select performs select to box space. @@ -271,8 +272,8 @@ func (conn *Connection) Ping() (Response, error) { // Deprecated: the method will be removed in the next major version, // use a SelectRequest object + Do() instead. func (conn *Connection) Select(space, index interface{}, offset, limit uint32, iterator Iter, - key interface{}) (Response, error) { - return conn.SelectAsync(space, index, offset, limit, iterator, key).GetResponse() + key interface{}) ([]interface{}, error) { + return conn.SelectAsync(space, index, offset, limit, iterator, key).Get() } // Insert performs insertion to box space. @@ -282,8 +283,8 @@ func (conn *Connection) Select(space, index interface{}, offset, limit uint32, i // // Deprecated: the method will be removed in the next major version, // use an InsertRequest object + Do() instead. -func (conn *Connection) Insert(space interface{}, tuple interface{}) (Response, error) { - return conn.InsertAsync(space, tuple).GetResponse() +func (conn *Connection) Insert(space interface{}, tuple interface{}) ([]interface{}, error) { + return conn.InsertAsync(space, tuple).Get() } // Replace performs "insert or replace" action to box space. @@ -293,8 +294,8 @@ func (conn *Connection) Insert(space interface{}, tuple interface{}) (Response, // // Deprecated: the method will be removed in the next major version, // use a ReplaceRequest object + Do() instead. -func (conn *Connection) Replace(space interface{}, tuple interface{}) (Response, error) { - return conn.ReplaceAsync(space, tuple).GetResponse() +func (conn *Connection) Replace(space interface{}, tuple interface{}) ([]interface{}, error) { + return conn.ReplaceAsync(space, tuple).Get() } // Delete performs deletion of a tuple by key. @@ -304,8 +305,8 @@ func (conn *Connection) Replace(space interface{}, tuple interface{}) (Response, // // Deprecated: the method will be removed in the next major version, // use a DeleteRequest object + Do() instead. -func (conn *Connection) Delete(space, index interface{}, key interface{}) (Response, error) { - return conn.DeleteAsync(space, index, key).GetResponse() +func (conn *Connection) Delete(space, index interface{}, key interface{}) ([]interface{}, error) { + return conn.DeleteAsync(space, index, key).Get() } // Update performs update of a tuple by key. @@ -315,8 +316,9 @@ func (conn *Connection) Delete(space, index interface{}, key interface{}) (Respo // // Deprecated: the method will be removed in the next major version, // use a UpdateRequest object + Do() instead. -func (conn *Connection) Update(space, index, key interface{}, ops *Operations) (Response, error) { - return conn.UpdateAsync(space, index, key, ops).GetResponse() +func (conn *Connection) Update(space, index, key interface{}, + ops *Operations) ([]interface{}, error) { + return conn.UpdateAsync(space, index, key, ops).Get() } // Upsert performs "update or insert" action of a tuple by key. @@ -326,8 +328,8 @@ func (conn *Connection) Update(space, index, key interface{}, ops *Operations) ( // // Deprecated: the method will be removed in the next major version, // use a UpsertRequest object + Do() instead. -func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) (Response, error) { - return conn.UpsertAsync(space, tuple, ops).GetResponse() +func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) ([]interface{}, error) { + return conn.UpsertAsync(space, tuple, ops).Get() } // Call calls registered Tarantool function. @@ -337,8 +339,8 @@ func (conn *Connection) Upsert(space, tuple interface{}, ops *Operations) (Respo // // Deprecated: the method will be removed in the next major version, // use a CallRequest object + Do() instead. -func (conn *Connection) Call(functionName string, args interface{}) (Response, error) { - return conn.CallAsync(functionName, args).GetResponse() +func (conn *Connection) Call(functionName string, args interface{}) ([]interface{}, error) { + return conn.CallAsync(functionName, args).Get() } // Call16 calls registered Tarantool function. @@ -349,8 +351,8 @@ func (conn *Connection) Call(functionName string, args interface{}) (Response, e // // Deprecated: the method will be removed in the next major version, // use a Call16Request object + Do() instead. -func (conn *Connection) Call16(functionName string, args interface{}) (Response, error) { - return conn.Call16Async(functionName, args).GetResponse() +func (conn *Connection) Call16(functionName string, args interface{}) ([]interface{}, error) { + return conn.Call16Async(functionName, args).Get() } // Call17 calls registered Tarantool function. @@ -360,8 +362,8 @@ func (conn *Connection) Call16(functionName string, args interface{}) (Response, // // Deprecated: the method will be removed in the next major version, // use a Call17Request object + Do() instead. -func (conn *Connection) Call17(functionName string, args interface{}) (Response, error) { - return conn.Call17Async(functionName, args).GetResponse() +func (conn *Connection) Call17(functionName string, args interface{}) ([]interface{}, error) { + return conn.Call17Async(functionName, args).Get() } // Eval passes Lua expression for evaluation. @@ -370,8 +372,8 @@ func (conn *Connection) Call17(functionName string, args interface{}) (Response, // // Deprecated: the method will be removed in the next major version, // use an EvalRequest object + Do() instead. -func (conn *Connection) Eval(expr string, args interface{}) (Response, error) { - return conn.EvalAsync(expr, args).GetResponse() +func (conn *Connection) Eval(expr string, args interface{}) ([]interface{}, error) { + return conn.EvalAsync(expr, args).Get() } // Execute passes sql expression to Tarantool for execution. @@ -381,8 +383,8 @@ func (conn *Connection) Eval(expr string, args interface{}) (Response, error) { // // Deprecated: the method will be removed in the next major version, // use an ExecuteRequest object + Do() instead. -func (conn *Connection) Execute(expr string, args interface{}) (Response, error) { - return conn.ExecuteAsync(expr, args).GetResponse() +func (conn *Connection) Execute(expr string, args interface{}) ([]interface{}, error) { + return conn.ExecuteAsync(expr, args).Get() } // single used for conn.GetTyped for decode one tuple. @@ -532,9 +534,20 @@ func (conn *Connection) EvalTyped(expr string, args interface{}, result interfac // use an ExecuteRequest object + Do() instead. func (conn *Connection) ExecuteTyped(expr string, args interface{}, result interface{}) (SQLInfo, []ColumnMetaData, error) { + var ( + sqlInfo SQLInfo + metaData []ColumnMetaData + ) + fut := conn.ExecuteAsync(expr, args) err := fut.GetTyped(&result) - return fut.resp.SQLInfo(), fut.resp.MetaData(), err + if resp, ok := fut.resp.(*ExecuteResponse); ok { + sqlInfo = resp.sqlInfo + metaData = resp.metaData + } else if err == nil { + err = fmt.Errorf("unexpected response type %T, want: *ExecuteResponse", fut.resp) + } + return sqlInfo, metaData, err } // SelectAsync sends select request to Tarantool and returns Future. @@ -807,6 +820,8 @@ type Request interface { Ctx() context.Context // Async returns true if the request does not expect response. Async() bool + // Response creates a response for current request type. + Response(header Header, body io.Reader) (Response, error) } // ConnectedRequest is an interface that provides the info about a Connection @@ -838,6 +853,15 @@ func (req *baseRequest) Ctx() context.Context { return req.ctx } +// Response creates a response for the baseRequest. +func (req *baseRequest) Response(header Header, body io.Reader) (Response, error) { + resp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &resp, nil +} + type spaceRequest struct { baseRequest space interface{} @@ -928,6 +952,15 @@ func (req authRequest) Body(res SchemaResolver, enc *msgpack.Encoder) error { return nil } +// Response creates a response for the authRequest. +func (req authRequest) Response(header Header, body io.Reader) (Response, error) { + resp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &resp, nil +} + // PingRequest helps you to create an execute request object for execution // by a Connection. type PingRequest struct { @@ -1070,6 +1103,15 @@ func (req *SelectRequest) Context(ctx context.Context) *SelectRequest { return req } +// Response creates a response for the SelectRequest. +func (req *SelectRequest) Response(header Header, body io.Reader) (Response, error) { + baseResp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &SelectResponse{BaseResponse: baseResp}, nil +} + // InsertRequest helps you to create an insert request object for execution // by a Connection. type InsertRequest struct { @@ -1469,6 +1511,15 @@ func (req *ExecuteRequest) Context(ctx context.Context) *ExecuteRequest { return req } +// Response creates a response for the ExecuteRequest. +func (req *ExecuteRequest) Response(header Header, body io.Reader) (Response, error) { + baseResp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &ExecuteResponse{BaseResponse: baseResp}, nil +} + // WatchOnceRequest synchronously fetches the value currently associated with a // specified notification key without subscribing to changes. type WatchOnceRequest struct { diff --git a/response.go b/response.go index 09145ee9d..61c77f413 100644 --- a/response.go +++ b/response.go @@ -2,6 +2,8 @@ package tarantool import ( "fmt" + "io" + "io/ioutil" "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" @@ -15,26 +17,62 @@ type Response interface { Decode() ([]interface{}, error) // DecodeTyped decodes a response into a given container res. DecodeTyped(res interface{}) error - - // Pos returns a position descriptor of the last selected tuple. - Pos() []byte - // MetaData returns meta-data. - MetaData() []ColumnMetaData - // SQLInfo returns sql info. - SQLInfo() SQLInfo } -// ConnResponse is a Response interface implementation. -// It is used for all request types. -type ConnResponse struct { +// BaseResponse is a base Response interface implementation. +type BaseResponse struct { header Header // data contains deserialized data for untyped requests. - data []interface{} + data []interface{} + buf smallBuf + decoded bool + decodedTyped bool +} + +func createBaseResponse(header Header, body io.Reader) (BaseResponse, error) { + if body == nil { + return BaseResponse{header: header}, nil + } + if buf, ok := body.(*smallBuf); ok { + return BaseResponse{header: header, buf: *buf}, nil + } + data, err := ioutil.ReadAll(body) + if err != nil { + return BaseResponse{}, err + } + return BaseResponse{header: header, buf: smallBuf{b: data}}, nil +} + +func (resp *BaseResponse) SetHeader(header Header) { + resp.header = header +} + +// SelectResponse is used for the select requests. +// It might contain a position descriptor of the last selected tuple. +// +// You need to cast to SelectResponse a response from SelectRequest. +type SelectResponse struct { + BaseResponse // pos contains a position descriptor of last selected tuple. - pos []byte + pos []byte +} + +// PrepareResponse is used for the prepare requests. +// It might contain meta-data and sql info. +// +// Be careful: now this is an alias for `ExecuteResponse`, +// but it could be changed in the future. +// You need to cast to PrepareResponse a response from PrepareRequest. +type PrepareResponse ExecuteResponse + +// ExecuteResponse is used for the execute requests. +// It might contain meta-data and sql info. +// +// You need to cast to ExecuteResponse a response from ExecuteRequest. +type ExecuteResponse struct { + BaseResponse metaData []ColumnMetaData sqlInfo SQLInfo - buf smallBuf } type ColumnMetaData struct { @@ -166,19 +204,118 @@ func decodeHeader(d *msgpack.Decoder, buf *smallBuf) (Header, error) { return decodedHeader, nil } -func (resp *ConnResponse) Decode() ([]interface{}, error) { +type decodeInfo struct { + stmtID uint64 + bindCount uint64 + serverProtocolInfo ProtocolInfo + errorExtendedInfo *BoxError + + decodedError string +} + +func (info *decodeInfo) parseData(resp *BaseResponse) error { + if info.stmtID != 0 { + stmt := &Prepared{ + StatementID: PreparedID(info.stmtID), + ParamCount: info.bindCount, + } + resp.data = []interface{}{stmt} + return nil + } + + // Tarantool may send only version >= 1. + if info.serverProtocolInfo.Version != ProtocolVersion(0) || + info.serverProtocolInfo.Features != nil { + if info.serverProtocolInfo.Version == ProtocolVersion(0) { + return fmt.Errorf("no protocol version provided in Id response") + } + if info.serverProtocolInfo.Features == nil { + return fmt.Errorf("no features provided in Id response") + } + resp.data = []interface{}{info.serverProtocolInfo} + return nil + } + return nil +} + +func decodeCommonField(d *msgpack.Decoder, cd int, data *[]interface{}, + info *decodeInfo) (bool, error) { + var feature iproto.Feature var err error - if resp.buf.Len() > 2 { - var decodedError string + switch iproto.Key(cd) { + case iproto.IPROTO_DATA: + var res interface{} + var ok bool + if res, err = d.DecodeInterface(); err != nil { + return false, err + } + if *data, ok = res.([]interface{}); !ok { + return false, fmt.Errorf("result is not array: %v", res) + } + case iproto.IPROTO_ERROR: + if info.errorExtendedInfo, err = decodeBoxError(d); err != nil { + return false, err + } + case iproto.IPROTO_ERROR_24: + if info.decodedError, err = d.DecodeString(); err != nil { + return false, err + } + case iproto.IPROTO_STMT_ID: + if info.stmtID, err = d.DecodeUint64(); err != nil { + return false, err + } + case iproto.IPROTO_BIND_COUNT: + if info.bindCount, err = d.DecodeUint64(); err != nil { + return false, err + } + case iproto.IPROTO_VERSION: + if err = d.Decode(&info.serverProtocolInfo.Version); err != nil { + return false, err + } + case iproto.IPROTO_FEATURES: + var larr int + if larr, err = d.DecodeArrayLen(); err != nil { + return false, err + } + + info.serverProtocolInfo.Features = make([]iproto.Feature, larr) + for i := 0; i < larr; i++ { + if err = d.Decode(&feature); err != nil { + return false, err + } + info.serverProtocolInfo.Features[i] = feature + } + case iproto.IPROTO_AUTH_TYPE: + var auth string + if auth, err = d.DecodeString(); err != nil { + return false, err + } + found := false + for _, a := range [...]Auth{ChapSha1Auth, PapSha256Auth} { + if auth == a.String() { + info.serverProtocolInfo.Auth = a + found = true + } + } + if !found { + return false, fmt.Errorf("unknown auth type %s", auth) + } + default: + return false, nil + } + return true, nil +} + +func (resp *BaseResponse) Decode() ([]interface{}, error) { + resp.decoded = true + var err error + if resp.buf.Len() > 2 { offset := resp.buf.Offset() defer resp.buf.Seek(offset) - var l, larr int - var stmtID, bindCount uint64 - var serverProtocolInfo ProtocolInfo - var feature iproto.Feature - var errorExtendedInfo *BoxError = nil + var l int + info := &decodeInfo{} d := msgpack.NewDecoder(&resp.buf) d.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { @@ -193,117 +330,171 @@ func (resp *ConnResponse) Decode() ([]interface{}, error) { if cd, err = smallInt(d, &resp.buf); err != nil { return nil, err } - switch iproto.Key(cd) { - case iproto.IPROTO_DATA: - var res interface{} - var ok bool - if res, err = d.DecodeInterface(); err != nil { - return nil, err - } - if resp.data, ok = res.([]interface{}); !ok { - return nil, fmt.Errorf("result is not array: %v", res) - } - case iproto.IPROTO_ERROR: - if errorExtendedInfo, err = decodeBoxError(d); err != nil { - return nil, err - } - case iproto.IPROTO_ERROR_24: - if decodedError, err = d.DecodeString(); err != nil { - return nil, err - } - case iproto.IPROTO_SQL_INFO: - if err = d.Decode(&resp.sqlInfo); err != nil { - return nil, err - } - case iproto.IPROTO_METADATA: - if err = d.Decode(&resp.metaData); err != nil { - return nil, err - } - case iproto.IPROTO_STMT_ID: - if stmtID, err = d.DecodeUint64(); err != nil { - return nil, err - } - case iproto.IPROTO_BIND_COUNT: - if bindCount, err = d.DecodeUint64(); err != nil { - return nil, err - } - case iproto.IPROTO_VERSION: - if err = d.Decode(&serverProtocolInfo.Version); err != nil { - return nil, err - } - case iproto.IPROTO_FEATURES: - if larr, err = d.DecodeArrayLen(); err != nil { + decoded, err := decodeCommonField(d, cd, &resp.data, info) + if err != nil { + return nil, err + } + if !decoded { + if err = d.Skip(); err != nil { return nil, err } + } + } + err = info.parseData(resp) + if err != nil { + return nil, err + } - serverProtocolInfo.Features = make([]iproto.Feature, larr) - for i := 0; i < larr; i++ { - if err = d.Decode(&feature); err != nil { - return nil, err + if info.decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + } + } + return resp.data, err +} + +func (resp *SelectResponse) Decode() ([]interface{}, error) { + resp.decoded = true + var err error + if resp.buf.Len() > 2 { + offset := resp.buf.Offset() + defer resp.buf.Seek(offset) + + var l int + info := &decodeInfo{} + + d := msgpack.NewDecoder(&resp.buf) + d.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) + + if l, err = d.DecodeMapLen(); err != nil { + return nil, err + } + for ; l > 0; l-- { + var cd int + if cd, err = smallInt(d, &resp.buf); err != nil { + return nil, err + } + decoded, err := decodeCommonField(d, cd, &resp.data, info) + if err != nil { + return nil, err + } + if !decoded { + switch iproto.Key(cd) { + case iproto.IPROTO_POSITION: + if resp.pos, err = d.DecodeBytes(); err != nil { + return nil, fmt.Errorf("unable to decode a position: %w", err) } - serverProtocolInfo.Features[i] = feature - } - case iproto.IPROTO_AUTH_TYPE: - var auth string - if auth, err = d.DecodeString(); err != nil { - return nil, err - } - found := false - for _, a := range [...]Auth{ChapSha1Auth, PapSha256Auth} { - if auth == a.String() { - serverProtocolInfo.Auth = a - found = true + default: + if err = d.Skip(); err != nil { + return nil, err } } - if !found { - return nil, fmt.Errorf("unknown auth type %s", auth) - } - case iproto.IPROTO_POSITION: - if resp.pos, err = d.DecodeBytes(); err != nil { - return nil, fmt.Errorf("unable to decode a position: %w", err) - } - default: - if err = d.Skip(); err != nil { - return nil, err - } } } - if stmtID != 0 { - stmt := &Prepared{ - StatementID: PreparedID(stmtID), - ParamCount: bindCount, - MetaData: resp.metaData, - } - resp.data = []interface{}{stmt} + err = info.parseData(&resp.BaseResponse) + if err != nil { + return nil, err + } + + if info.decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} } + } + return resp.data, err +} + +func (resp *ExecuteResponse) Decode() ([]interface{}, error) { + resp.decoded = true + var err error + if resp.buf.Len() > 2 { + offset := resp.buf.Offset() + defer resp.buf.Seek(offset) + + var l int + info := &decodeInfo{} + + d := msgpack.NewDecoder(&resp.buf) + d.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) - // Tarantool may send only version >= 1 - if serverProtocolInfo.Version != ProtocolVersion(0) || serverProtocolInfo.Features != nil { - if serverProtocolInfo.Version == ProtocolVersion(0) { - return nil, fmt.Errorf("no protocol version provided in Id response") + if l, err = d.DecodeMapLen(); err != nil { + return nil, err + } + for ; l > 0; l-- { + var cd int + if cd, err = smallInt(d, &resp.buf); err != nil { + return nil, err } - if serverProtocolInfo.Features == nil { - return nil, fmt.Errorf("no features provided in Id response") + decoded, err := decodeCommonField(d, cd, &resp.data, info) + if err != nil { + return nil, err + } + if !decoded { + switch iproto.Key(cd) { + case iproto.IPROTO_SQL_INFO: + if err = d.Decode(&resp.sqlInfo); err != nil { + return nil, err + } + case iproto.IPROTO_METADATA: + if err = d.Decode(&resp.metaData); err != nil { + return nil, err + } + default: + if err = d.Skip(); err != nil { + return nil, err + } + } } - resp.data = []interface{}{serverProtocolInfo} + } + err = info.parseData(&resp.BaseResponse) + if err != nil { + return nil, err } - if decodedError != "" { + if info.decodedError != "" { resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), decodedError, errorExtendedInfo} + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} } } return resp.data, err } -func (resp *ConnResponse) DecodeTyped(res interface{}) error { +func decodeTypedCommonField(d *msgpack.Decoder, res interface{}, cd int, + info *decodeInfo) (bool, error) { + var err error + + switch iproto.Key(cd) { + case iproto.IPROTO_DATA: + if err = d.Decode(res); err != nil { + return false, err + } + case iproto.IPROTO_ERROR: + if info.errorExtendedInfo, err = decodeBoxError(d); err != nil { + return false, err + } + case iproto.IPROTO_ERROR_24: + if info.decodedError, err = d.DecodeString(); err != nil { + return false, err + } + default: + return false, nil + } + return true, nil +} + +func (resp *BaseResponse) DecodeTyped(res interface{}) error { + resp.decodedTyped = true + var err error if resp.buf.Len() > 0 { offset := resp.buf.Offset() defer resp.buf.Seek(offset) - var errorExtendedInfo *BoxError = nil - + info := &decodeInfo{} var l int d := msgpack.NewDecoder(&resp.buf) @@ -314,69 +505,167 @@ func (resp *ConnResponse) DecodeTyped(res interface{}) error { if l, err = d.DecodeMapLen(); err != nil { return err } - var decodedError string for ; l > 0; l-- { var cd int if cd, err = smallInt(d, &resp.buf); err != nil { return err } - switch iproto.Key(cd) { - case iproto.IPROTO_DATA: - if err = d.Decode(res); err != nil { - return err - } - case iproto.IPROTO_ERROR: - if errorExtendedInfo, err = decodeBoxError(d); err != nil { - return err - } - case iproto.IPROTO_ERROR_24: - if decodedError, err = d.DecodeString(); err != nil { - return err - } - case iproto.IPROTO_SQL_INFO: - if err = d.Decode(&resp.sqlInfo); err != nil { - return err - } - case iproto.IPROTO_METADATA: - if err = d.Decode(&resp.metaData); err != nil { + decoded, err := decodeTypedCommonField(d, res, cd, info) + if err != nil { + return err + } + if !decoded { + if err = d.Skip(); err != nil { return err } - case iproto.IPROTO_POSITION: - if resp.pos, err = d.DecodeBytes(); err != nil { - return fmt.Errorf("unable to decode a position: %w", err) + } + } + if info.decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + } + } + return err +} + +func (resp *SelectResponse) DecodeTyped(res interface{}) error { + resp.decodedTyped = true + + var err error + if resp.buf.Len() > 0 { + offset := resp.buf.Offset() + defer resp.buf.Seek(offset) + + info := &decodeInfo{} + var l int + + d := msgpack.NewDecoder(&resp.buf) + d.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) + + if l, err = d.DecodeMapLen(); err != nil { + return err + } + for ; l > 0; l-- { + var cd int + if cd, err = smallInt(d, &resp.buf); err != nil { + return err + } + decoded, err := decodeTypedCommonField(d, res, cd, info) + if err != nil { + return err + } + if !decoded { + switch iproto.Key(cd) { + case iproto.IPROTO_POSITION: + if resp.pos, err = d.DecodeBytes(); err != nil { + return fmt.Errorf("unable to decode a position: %w", err) + } + default: + if err = d.Skip(); err != nil { + return err + } } - default: - if err = d.Skip(); err != nil { - return err + } + } + if info.decodedError != "" { + resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + } + } + return err +} + +func (resp *ExecuteResponse) DecodeTyped(res interface{}) error { + resp.decodedTyped = true + + var err error + if resp.buf.Len() > 0 { + offset := resp.buf.Offset() + defer resp.buf.Seek(offset) + + info := &decodeInfo{} + var l int + + d := msgpack.NewDecoder(&resp.buf) + d.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { + return dec.DecodeUntypedMap() + }) + + if l, err = d.DecodeMapLen(); err != nil { + return err + } + for ; l > 0; l-- { + var cd int + if cd, err = smallInt(d, &resp.buf); err != nil { + return err + } + decoded, err := decodeTypedCommonField(d, res, cd, info) + if err != nil { + return err + } + if !decoded { + switch iproto.Key(cd) { + case iproto.IPROTO_SQL_INFO: + if err = d.Decode(&resp.sqlInfo); err != nil { + return err + } + case iproto.IPROTO_METADATA: + if err = d.Decode(&resp.metaData); err != nil { + return err + } + default: + if err = d.Skip(); err != nil { + return err + } } } } - if decodedError != "" { + if info.decodedError != "" { resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), decodedError, errorExtendedInfo} + err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} } } return err } -func (resp *ConnResponse) Header() Header { +func (resp *BaseResponse) Header() Header { return resp.header } -func (resp *ConnResponse) Pos() []byte { - return resp.pos +// Pos returns a position descriptor of the last selected tuple for the SelectResponse. +// If the response was not decoded, this method will call Decode(). +func (resp *SelectResponse) Pos() ([]byte, error) { + var err error + if !resp.decoded && !resp.decodedTyped { + _, err = resp.Decode() + } + return resp.pos, err } -func (resp *ConnResponse) MetaData() []ColumnMetaData { - return resp.metaData +// MetaData returns ExecuteResponse meta-data. +// If the response was not decoded, this method will call Decode(). +func (resp *ExecuteResponse) MetaData() ([]ColumnMetaData, error) { + var err error + if !resp.decoded && !resp.decodedTyped { + _, err = resp.Decode() + } + return resp.metaData, err } -func (resp *ConnResponse) SQLInfo() SQLInfo { - return resp.sqlInfo +// SQLInfo returns ExecuteResponse sql info. +// If the response was not decoded, this method will call Decode(). +func (resp *ExecuteResponse) SQLInfo() (SQLInfo, error) { + var err error + if !resp.decoded && !resp.decodedTyped { + _, err = resp.Decode() + } + return resp.sqlInfo, err } // String implements Stringer interface. -func (resp *ConnResponse) String() (str string) { +func (resp *BaseResponse) String() (str string) { if resp.header.Code == OkCode { return fmt.Sprintf("<%d OK %v>", resp.header.RequestId, resp.data) } diff --git a/settings/example_test.go b/settings/example_test.go index 0a1dcc9b7..e51cadef0 100644 --- a/settings/example_test.go +++ b/settings/example_test.go @@ -69,12 +69,23 @@ func Example_sqlFullColumnNames() { // Get some data with SQL query. req = tarantool.NewExecuteRequest("SELECT x FROM example WHERE id = 1;") - _, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() if err != nil { fmt.Printf("error on select: %v\n", err) return } - metaData := resp.MetaData() + + exResp, ok := resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + + metaData, err := exResp.MetaData() + if err != nil { + fmt.Printf("error on getting MetaData: %v\n", err) + return + } // Show response metadata. fmt.Printf("full column name: %v\n", metaData[0].FieldName) @@ -86,12 +97,21 @@ func Example_sqlFullColumnNames() { } // Get some data with SQL query. - _, err = conn.Do(req).Get() + resp, err = conn.Do(req).GetResponse() if err != nil { fmt.Printf("error on select: %v\n", err) return } - metaData = resp.MetaData() + exResp, ok = resp.(*tarantool.ExecuteResponse) + if !ok { + fmt.Printf("wrong response type") + return + } + metaData, err = exResp.MetaData() + if err != nil { + fmt.Printf("error on getting MetaData: %v\n", err) + return + } // Show response metadata. fmt.Printf("short column name: %v\n", metaData[0].FieldName) } diff --git a/settings/request.go b/settings/request.go index 02252fe47..1c106dc8d 100644 --- a/settings/request.go +++ b/settings/request.go @@ -60,6 +60,7 @@ package settings import ( "context" + "io" "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" @@ -107,6 +108,12 @@ func (req *SetRequest) Async() bool { return req.impl.Async() } +// Response creates a response for the SetRequest. +func (req *SetRequest) Response(header tarantool.Header, + body io.Reader) (tarantool.Response, error) { + return req.impl.Response(header, body) +} + // GetRequest helps to get session settings. type GetRequest struct { impl *tarantool.SelectRequest @@ -147,6 +154,12 @@ func (req *GetRequest) Async() bool { return req.impl.Async() } +// Response creates a response for the GetRequest. +func (req *GetRequest) Response(header tarantool.Header, + body io.Reader) (tarantool.Response, error) { + return req.impl.Response(header, body) +} + // NewErrorMarshalingEnabledSetRequest creates a request to // update current session ErrorMarshalingEnabled setting. func NewErrorMarshalingEnabledSetRequest(value bool) *SetRequest { diff --git a/settings/tarantool_test.go b/settings/tarantool_test.go index 6ca64455a..56cee33ce 100644 --- a/settings/tarantool_test.go +++ b/settings/tarantool_test.go @@ -114,7 +114,11 @@ func TestSQLDefaultEngineSetting(t *testing.T) { resp, err := conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Check new space engine. eval := tarantool.NewEvalRequest("return box.space['T1_VINYL'].engine") @@ -137,7 +141,11 @@ func TestSQLDefaultEngineSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.True(t, ok, "wrong response type") + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Check new space engine. eval = tarantool.NewEvalRequest("return box.space['T2_MEMTX'].engine") @@ -161,7 +169,11 @@ func TestSQLDeferForeignKeysSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Create a space with reference to the parent space. exec = tarantool.NewExecuteRequest( @@ -169,7 +181,11 @@ func TestSQLDeferForeignKeysSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) deferEval := ` box.begin() @@ -229,14 +245,22 @@ func TestSQLFullColumnNamesSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO FKNAME VALUES (1, 1);") resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Disable displaying full column names in metadata. data, err := conn.Do(NewSQLFullColumnNamesSetRequest(false)).Get() @@ -253,7 +277,11 @@ func TestSQLFullColumnNamesSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "X", resp.MetaData()[0].FieldName) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + metaData, err := exResp.MetaData() + require.Nil(t, err) + require.Equal(t, "X", metaData[0].FieldName) // Enable displaying full column names in metadata. data, err = conn.Do(NewSQLFullColumnNamesSetRequest(true)).Get() @@ -270,7 +298,11 @@ func TestSQLFullColumnNamesSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "FKNAME.X", resp.MetaData()[0].FieldName) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + metaData, err = exResp.MetaData() + require.Nil(t, err) + require.Equal(t, "FKNAME.X", metaData[0].FieldName) } func TestSQLFullMetadataSetting(t *testing.T) { @@ -287,14 +319,22 @@ func TestSQLFullMetadataSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO fmt VALUES (1, 1);") resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Disable displaying additional fields in metadata. data, err := conn.Do(NewSQLFullMetadataSetRequest(false)).Get() @@ -311,7 +351,11 @@ func TestSQLFullMetadataSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "", resp.MetaData()[0].FieldSpan) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + metaData, err := exResp.MetaData() + require.Nil(t, err) + require.Equal(t, "", metaData[0].FieldSpan) // Enable displaying full column names in metadata. data, err = conn.Do(NewSQLFullMetadataSetRequest(true)).Get() @@ -328,7 +372,11 @@ func TestSQLFullMetadataSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, "x", resp.MetaData()[0].FieldSpan) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + metaData, err = exResp.MetaData() + require.Nil(t, err) + require.Equal(t, "x", metaData[0].FieldSpan) } func TestSQLParserDebugSetting(t *testing.T) { @@ -378,14 +426,22 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO rec VALUES(1, 1, 2);") resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Create a recursive trigger (with infinite depth). exec = tarantool.NewExecuteRequest(` @@ -395,7 +451,11 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Enable SQL recursive triggers. data, err := conn.Do(NewSQLRecursiveTriggersSetRequest(true)).Get() @@ -429,7 +489,11 @@ func TestSQLRecursiveTriggersSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) } func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { @@ -446,20 +510,32 @@ func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err := exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Fill it with some data. exec = tarantool.NewExecuteRequest("INSERT INTO data VALUES('1');") resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) exec = tarantool.NewExecuteRequest("INSERT INTO data VALUES('2');") resp, err = conn.Do(exec).GetResponse() require.Nil(t, err) require.NotNil(t, resp) - require.Equal(t, uint64(1), resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*tarantool.ExecuteResponse) + require.True(t, ok, "wrong response type") + sqlInfo, err = exResp.SQLInfo() + require.Nil(t, err) + require.Equal(t, uint64(1), sqlInfo.AffectedCount) // Disable reverse order in unordered selects. data, err := conn.Do(NewSQLReverseUnorderedSelectsSetRequest(false)).Get() diff --git a/tarantool_test.go b/tarantool_test.go index aa3f248f9..3b8bc4653 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -699,6 +699,35 @@ func BenchmarkSQLSerial(b *testing.B) { } } +type mockRequest struct { + conn *Connection +} + +func (req *mockRequest) Type() iproto.Type { + return iproto.Type(0) +} + +func (req *mockRequest) Async() bool { + return false +} + +func (req *mockRequest) Body(resolver SchemaResolver, enc *msgpack.Encoder) error { + return nil +} + +func (req *mockRequest) Conn() *Connection { + return req.conn +} + +func (req *mockRequest) Ctx() context.Context { + return nil +} + +func (req *mockRequest) Response(header Header, + body io.Reader) (Response, error) { + return nil, fmt.Errorf("some error") +} + func TestNetDialer(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -821,39 +850,25 @@ func TestFutureMultipleGetTypedWithError(t *testing.T) { /////////////////// func TestClient(t *testing.T) { - var resp Response var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Ping - resp, err = conn.Ping() + data, err := conn.Ping() if err != nil { t.Fatalf("Failed to Ping: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Ping") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") + if data != nil { + t.Fatalf("Response data is not nil after Ping") } // Insert - resp, err = conn.Insert(spaceNo, []interface{}{uint(1), "hello", "world"}) + data, err = conn.Insert(spaceNo, []interface{}{uint(1), "hello", "world"}) if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Insert") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err := resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 1 { t.Errorf("Response Body len != 1") } @@ -870,30 +885,19 @@ func TestClient(t *testing.T) { t.Errorf("Unexpected body of Insert (1)") } } - resp, err = conn.Insert(spaceNo, &Tuple{Id: 1, Msg: "hello", Name: "world"}) + data, err = conn.Insert(spaceNo, &Tuple{Id: 1, Msg: "hello", Name: "world"}) if tntErr, ok := err.(Error); !ok || tntErr.Code != iproto.ER_TUPLE_FOUND { t.Errorf("Expected %s but got: %v", iproto.ER_TUPLE_FOUND, err) } - data, _ = resp.Decode() if len(data) != 0 { t.Errorf("Response Body len != 0") } // Delete - resp, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(1)}) + data, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(1)}) if err != nil { t.Fatalf("Failed to Delete: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Delete") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 1 { t.Errorf("Response Body len != 1") } @@ -910,46 +914,26 @@ func TestClient(t *testing.T) { t.Errorf("Unexpected body of Delete (1)") } } - resp, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(101)}) + data, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(101)}) if err != nil { t.Fatalf("Failed to Delete: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Delete") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 0 { t.Errorf("Response Data len != 0") } // Replace - resp, err = conn.Replace(spaceNo, []interface{}{uint(2), "hello", "world"}) + data, err = conn.Replace(spaceNo, []interface{}{uint(2), "hello", "world"}) if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp == nil { + if data == nil { t.Fatalf("Response is nil after Replace") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - resp, err = conn.Replace(spaceNo, []interface{}{uint(2), "hi", "planet"}) + data, err = conn.Replace(spaceNo, []interface{}{uint(2), "hi", "planet"}) if err != nil { t.Fatalf("Failed to Replace (duplicate): %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Replace (duplicate)") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 1 { t.Errorf("Response Data len != 1") } @@ -968,21 +952,11 @@ func TestClient(t *testing.T) { } // Update - resp, err = conn.Update(spaceNo, indexNo, []interface{}{uint(2)}, + data, err = conn.Update(spaceNo, indexNo, []interface{}{uint(2)}, NewOperations().Assign(1, "bye").Delete(2, 1)) if err != nil { t.Fatalf("Failed to Update: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Update") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 1 { t.Errorf("Response Data len != 1") } @@ -1001,56 +975,39 @@ func TestClient(t *testing.T) { } // Upsert - resp, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, + data, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, NewOperations().Add(1, 1)) if err != nil { t.Fatalf("Failed to Upsert (insert): %s", err.Error()) } - if resp == nil { + if data == nil { t.Fatalf("Response is nil after Upsert (insert)") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - resp, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, + data, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, NewOperations().Add(1, 1)) if err != nil { t.Fatalf("Failed to Upsert (update): %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Upsert (update)") } // Select for i := 10; i < 20; i++ { - resp, err = conn.Replace(spaceNo, []interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) + data, err = conn.Replace(spaceNo, []interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - _, err := resp.Decode() - if err != nil { - t.Errorf("Failed to replace: %s", err.Error()) + if data == nil { + t.Errorf("Response is nil after Replace") } } - resp, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}) + data, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}) if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 1 { - t.Errorf("Response Data len != 1") + t.Fatalf("Response Data len != 1") } if tpl, ok := data[0].([]interface{}); !ok { t.Errorf("Unexpected body of Select") @@ -1064,20 +1021,10 @@ func TestClient(t *testing.T) { } // Select empty - resp, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(30)}) + data, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(30)}) if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Select") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) != 0 { t.Errorf("Response Data len != 0") } @@ -1137,70 +1084,36 @@ func TestClient(t *testing.T) { } // Call16 - resp, err = conn.Call16("box.info", []interface{}{"box.schema.SPACE_ID"}) + data, err = conn.Call16("box.info", []interface{}{"box.schema.SPACE_ID"}) if err != nil { t.Fatalf("Failed to Call16: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Call16") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") } // Call16 vs Call17 - resp, err = conn.Call16("simple_concat", []interface{}{"1"}) + data, err = conn.Call16("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call16") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if val, ok := data[0].([]interface{})[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", data) } - resp, err = conn.Call17("simple_concat", []interface{}{"1"}) + data, err = conn.Call17("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if val, ok := data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", data) } // Eval - resp, err = conn.Eval("return 5 + 6", []interface{}{}) + data, err = conn.Eval("return 5 + 6", []interface{}{}) if err != nil { t.Fatalf("Failed to Eval: %s", err.Error()) } - if resp == nil { - t.Fatalf("Response is nil after Eval") - } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } - data, err = resp.Decode() - if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) - } if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") } @@ -1260,6 +1173,9 @@ func TestClientSessionPush(t *testing.T) { } for i := 0; i < len(its); i++ { + pushCnt := uint64(0) + respCnt := uint64(0) + it = its[i] for it.Next() { resp := it.Value() @@ -1267,9 +1183,6 @@ func TestClientSessionPush(t *testing.T) { t.Errorf("Response is empty after it.Next() == true") break } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } data, err := resp.Decode() if err != nil { t.Errorf("Failed to Decode: %s", err.Error()) @@ -1279,11 +1192,32 @@ func TestClientSessionPush(t *testing.T) { t.Errorf("Response.Data is empty after CallAsync") break } + if it.IsPush() { + pushCnt += 1 + val, err := test_helpers.ConvertUint64(data[0]) + if err != nil || val != pushCnt { + t.Errorf("Unexpected push data = %v", data) + } + } else { + respCnt += 1 + val, err := test_helpers.ConvertUint64(data[0]) + if err != nil || val != pushMax { + t.Errorf("Result is not %d: %v", pushMax, data) + } + } } if err = it.Err(); err != nil { t.Errorf("An unexpected iteration error: %s", err.Error()) } + + if pushCnt != pushMax { + t.Errorf("Expect %d pushes but got %d", pushMax, pushCnt) + } + + if respCnt != 1 { + t.Errorf("Expect %d responses but got %d", 1, respCnt) + } } // We can collect original responses after iterations. @@ -1472,7 +1406,8 @@ func TestSQL(t *testing.T) { defer conn.Close() for i, test := range testCases { - resp, err := conn.Execute(test.Query, test.Args) + req := NewExecuteRequest(test.Query).Args(test.Args) + resp, err := conn.Do(req).GetResponse() assert.NoError(t, err, "Failed to Execute, query: %s", test.Query) assert.NotNil(t, resp, "Response is nil after Execute\nQuery number: %d", i) data, err := resp.Decode() @@ -1480,21 +1415,18 @@ func TestSQL(t *testing.T) { for j := range data { assert.Equal(t, data[j], test.data[j], "Response data is wrong") } - assert.Equal(t, resp.SQLInfo().AffectedCount, test.sqlInfo.AffectedCount, + exResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err := exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + assert.Equal(t, sqlInfo.AffectedCount, test.sqlInfo.AffectedCount, "Affected count is wrong") errorMsg := "Response Metadata is wrong" - metaData := resp.MetaData() + metaData, err := exResp.MetaData() + assert.Nil(t, err, "Error while getting MetaData") for j := range metaData { - assert.Equal(t, metaData[j].FieldIsAutoincrement, - test.metaData[j].FieldIsAutoincrement, errorMsg) - assert.Equal(t, metaData[j].FieldIsNullable, - test.metaData[j].FieldIsNullable, errorMsg) - assert.Equal(t, metaData[j].FieldCollation, - test.metaData[j].FieldCollation, errorMsg) - assert.Equal(t, metaData[j].FieldName, test.metaData[j].FieldName, errorMsg) - assert.Equal(t, metaData[j].FieldSpan, test.metaData[j].FieldSpan, errorMsg) - assert.Equal(t, metaData[j].FieldType, test.metaData[j].FieldType, errorMsg) + assert.Equal(t, metaData[j], test.metaData[j], errorMsg) } } } @@ -1575,7 +1507,8 @@ func TestSQLBindings(t *testing.T) { } for _, bind := range namedSQLBinds { - resp, err := conn.Execute(selectNamedQuery2, bind) + req := NewExecuteRequest(selectNamedQuery2).Args(bind) + resp, err := conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } @@ -1589,7 +1522,10 @@ func TestSQLBindings(t *testing.T) { if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with named arguments failed") } - metaData := resp.MetaData() + exResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + metaData, err := exResp.MetaData() + assert.Nil(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1598,7 +1534,8 @@ func TestSQLBindings(t *testing.T) { } } - resp, err := conn.Execute(selectPosQuery2, sqlBind5) + req := NewExecuteRequest(selectPosQuery2).Args(sqlBind5) + resp, err := conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } @@ -1612,7 +1549,10 @@ func TestSQLBindings(t *testing.T) { if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") } - metaData := resp.MetaData() + exResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + metaData, err := exResp.MetaData() + assert.Nil(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1620,7 +1560,8 @@ func TestSQLBindings(t *testing.T) { t.Error("Wrong metadata") } - resp, err = conn.Execute(mixedQuery, sqlBind6) + req = NewExecuteRequest(mixedQuery).Args(sqlBind6) + resp, err = conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } @@ -1634,7 +1575,10 @@ func TestSQLBindings(t *testing.T) { if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") } - metaData = resp.MetaData() + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + metaData, err = exResp.MetaData() + assert.Nil(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1646,81 +1590,131 @@ func TestSQLBindings(t *testing.T) { func TestStressSQL(t *testing.T) { test_helpers.SkipIfSQLUnsupported(t) - var resp Response - conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() - resp, err := conn.Execute(createTableQuery, []interface{}{}) + req := NewExecuteRequest(createTableQuery) + resp, err := conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.SQLInfo().AffectedCount != 1 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err := exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + if sqlInfo.AffectedCount != 1 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } // create table with the same name - resp, err = conn.Execute(createTableQuery, []interface{}{}) - if err == nil { - t.Fatal("Unexpected lack of error") + req = NewExecuteRequest(createTableQuery) + resp, err = conn.Do(req).GetResponse() + if err != nil { + t.Fatalf("Failed to create an Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } + _, err = resp.Decode() + assert.NotNil(t, err, "Expected error while decoding") + + tntErr, ok := err.(Error) + assert.True(t, ok) + assert.Equal(t, iproto.ER_SPACE_EXISTS, tntErr.Code) + if iproto.Error(resp.Header().Code) != iproto.ER_SPACE_EXISTS { + t.Fatalf("Unexpected response code: %d", resp.Header().Code) + } - if resp.SQLInfo().AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.Nil(t, err, "Unexpected error") + if sqlInfo.AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } // execute with nil argument - resp, err = conn.Execute(createTableQuery, nil) - if err == nil { - t.Fatal("Unexpected lack of error") + req = NewExecuteRequest(createTableQuery).Args(nil) + resp, err = conn.Do(req).GetResponse() + if err != nil { + t.Fatalf("Failed to create an Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.SQLInfo().AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + if resp.Header().Code == OkCode { + t.Fatal("Unexpected successful Execute") + } + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.NotNil(t, err, "Expected an error") + if sqlInfo.AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } // execute with zero string - resp, err = conn.Execute("", []interface{}{}) - if err == nil { - t.Fatal("Unexpected lack of error") + req = NewExecuteRequest("") + resp, err = conn.Do(req).GetResponse() + if err != nil { + t.Fatalf("Failed to create an Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.SQLInfo().AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + if resp.Header().Code == OkCode { + t.Fatal("Unexpected successful Execute") + } + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.NotNil(t, err, "Expected an error") + if sqlInfo.AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } // drop table query - resp, err = conn.Execute(dropQuery2, []interface{}{}) + req = NewExecuteRequest(dropQuery2) + resp, err = conn.Do(req).GetResponse() if err != nil { t.Fatalf("Failed to Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.SQLInfo().AffectedCount != 1 { - t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + if sqlInfo.AffectedCount != 1 { + t.Errorf("Incorrect count of dropped spaces: %d", sqlInfo.AffectedCount) } // drop the same table - resp, err = conn.Execute(dropQuery2, []interface{}{}) - if err == nil { - t.Fatal("Unexpected lack of error") + req = NewExecuteRequest(dropQuery2) + resp, err = conn.Do(req).GetResponse() + if err != nil { + t.Fatalf("Failed to create an Execute: %s", err.Error()) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.SQLInfo().AffectedCount != 0 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + if resp.Header().Code == OkCode { + t.Fatal("Unexpected successful Execute") + } + _, err = resp.Decode() + if err == nil { + t.Fatal("Unexpected lack of error") + } + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + if sqlInfo.AffectedCount != 0 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } } @@ -1749,7 +1743,10 @@ func TestNewPrepared(t *testing.T) { if reflect.DeepEqual(data[0], []interface{}{1, "test"}) { t.Error("Select with named arguments failed") } - metaData := resp.MetaData() + prepResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + metaData, err := prepResp.MetaData() + assert.Nil(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1786,7 +1783,7 @@ func TestNewPrepared(t *testing.T) { if len(data) == 0 { t.Errorf("failed to prepare: response Data has no elements") } - stmt, ok := data[0].(*Prepared) + stmt, ok = data[0].(*Prepared) if !ok { t.Errorf("failed to prepare: failed to cast the response Data to Prepared object") } @@ -1811,6 +1808,18 @@ func TestConnection_DoWithStrangerConn(t *testing.T) { } } +func TestConnection_SetResponse_failed(t *testing.T) { + conn := test_helpers.ConnectWithValidation(t, dialer, opts) + defer conn.Close() + + req := mockRequest{conn} + fut := conn.Do(&req) + + data, err := fut.Get() + assert.EqualError(t, err, "failed to set response: some error") + assert.Nil(t, data) +} + func TestGetSchema(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() @@ -2043,85 +2052,82 @@ func TestSchema_IsNullable(t *testing.T) { } func TestClientNamed(t *testing.T) { - var resp Response - var err error - conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() // Insert - resp, err = conn.Insert(spaceName, []interface{}{uint(1001), "hello2", "world2"}) + data, err := conn.Insert(spaceName, []interface{}{uint(1001), "hello2", "world2"}) if err != nil { t.Fatalf("Failed to Insert: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Insert") } // Delete - resp, err = conn.Delete(spaceName, indexName, []interface{}{uint(1001)}) + data, err = conn.Delete(spaceName, indexName, []interface{}{uint(1001)}) if err != nil { t.Fatalf("Failed to Delete: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Delete") } // Replace - resp, err = conn.Replace(spaceName, []interface{}{uint(1002), "hello", "world"}) + data, err = conn.Replace(spaceName, []interface{}{uint(1002), "hello", "world"}) if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Replace") } // Update - resp, err = conn.Update(spaceName, indexName, + data, err = conn.Update(spaceName, indexName, []interface{}{ uint(1002)}, NewOperations().Assign(1, "buy").Delete(2, 1)) if err != nil { t.Fatalf("Failed to Update: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Update") } // Upsert - resp, err = conn.Upsert(spaceName, + data, err = conn.Upsert(spaceName, []interface{}{uint(1003), 1}, NewOperations().Add(1, 1)) if err != nil { t.Fatalf("Failed to Upsert (insert): %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Upsert (insert)") } - resp, err = conn.Upsert(spaceName, + data, err = conn.Upsert(spaceName, []interface{}{uint(1003), 1}, NewOperations().Add(1, 1)) if err != nil { t.Fatalf("Failed to Upsert (update): %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Upsert (update)") } // Select for i := 1010; i < 1020; i++ { - resp, err = conn.Replace(spaceName, + data, err = conn.Replace(spaceName, []interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) if err != nil { t.Fatalf("Failed to Replace: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Replace") } } - resp, err = conn.Select(spaceName, indexName, 0, 1, IterEq, []interface{}{uint(1010)}) + data, err = conn.Select(spaceName, indexName, 0, 1, IterEq, []interface{}{uint(1010)}) if err != nil { t.Fatalf("Failed to Select: %s", err.Error()) } - if resp == nil { + if data == nil { t.Errorf("Response is nil after Select") } @@ -2375,9 +2381,6 @@ func TestClientRequestObjects(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } data, err = resp.Decode() if err != nil { t.Fatalf("Failed to Decode: %s", err.Error()) @@ -2385,8 +2388,12 @@ func TestClientRequestObjects(t *testing.T) { if len(data) != 0 { t.Fatalf("Response Body len != 0") } - if resp.SQLInfo().AffectedCount != 1 { - t.Errorf("Incorrect count of created spaces: %d", resp.SQLInfo().AffectedCount) + exResp, ok := resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err := exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + if sqlInfo.AffectedCount != 1 { + t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } req = NewExecuteRequest(dropQuery2) @@ -2397,9 +2404,6 @@ func TestClientRequestObjects(t *testing.T) { if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Pos() != nil { - t.Errorf("Response should not have a position") - } data, err = resp.Decode() if err != nil { t.Fatalf("Failed to Decode: %s", err.Error()) @@ -2407,8 +2411,12 @@ func TestClientRequestObjects(t *testing.T) { if len(data) != 0 { t.Fatalf("Response Body len != 0") } - if resp.SQLInfo().AffectedCount != 1 { - t.Errorf("Incorrect count of dropped spaces: %d", resp.SQLInfo().AffectedCount) + exResp, ok = resp.(*ExecuteResponse) + assert.True(t, ok, "Got wrong response type") + sqlInfo, err = exResp.SQLInfo() + assert.Nil(t, err, "Error while getting SQLInfo") + if sqlInfo.AffectedCount != 1 { + t.Errorf("Incorrect count of dropped spaces: %d", sqlInfo.AffectedCount) } } @@ -2425,7 +2433,7 @@ func testConnectionDoSelectRequestPrepare(t *testing.T, conn Connector) { } func testConnectionDoSelectRequestCheck(t *testing.T, - resp Response, err error, pos bool, dataLen int, firstKey uint64) { + resp *SelectResponse, err error, pos bool, dataLen int, firstKey uint64) { t.Helper() if err != nil { @@ -2434,10 +2442,14 @@ func testConnectionDoSelectRequestCheck(t *testing.T, if resp == nil { t.Fatalf("Response is nil after Select") } - if !pos && resp.Pos() != nil { + respPos, err := resp.Pos() + if err != nil { + t.Errorf("Error while getting Pos: %s", err.Error()) + } + if !pos && respPos != nil { t.Errorf("Response should not have a position descriptor") } - if pos && resp.Pos() == nil { + if pos && respPos == nil { t.Fatalf("A response must have a position descriptor") } data, err := resp.Decode() @@ -2482,7 +2494,10 @@ func TestConnectionDoSelectRequest(t *testing.T) { Key([]interface{}{uint(1010)}) resp, err := conn.Do(req).GetResponse() - testConnectionDoSelectRequestCheck(t, resp, err, false, 10, 1010) + selResp, ok := resp.(*SelectResponse) + assert.True(t, ok, "Got wrong response type") + + testConnectionDoSelectRequestCheck(t, selResp, err, false, 10, 1010) } func TestConnectionDoWatchOnceRequest(t *testing.T) { @@ -2542,7 +2557,10 @@ func TestConnectionDoSelectRequest_fetch_pos(t *testing.T) { Key([]interface{}{uint(1010)}) resp, err := conn.Do(req).GetResponse() - testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1010) + selResp, ok := resp.(*SelectResponse) + assert.True(t, ok, "Got wrong response type") + + testConnectionDoSelectRequestCheck(t, selResp, err, true, 2, 1010) } func TestConnectDoSelectRequest_after_tuple(t *testing.T) { @@ -2562,7 +2580,10 @@ func TestConnectDoSelectRequest_after_tuple(t *testing.T) { After([]interface{}{uint(1012)}) resp, err := conn.Do(req).GetResponse() - testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1013) + selResp, ok := resp.(*SelectResponse) + assert.True(t, ok, "Got wrong response type") + + testConnectionDoSelectRequestCheck(t, selResp, err, true, 2, 1013) } func TestConnectionDoSelectRequest_pagination_pos(t *testing.T) { @@ -2581,28 +2602,29 @@ func TestConnectionDoSelectRequest_pagination_pos(t *testing.T) { Key([]interface{}{uint(1010)}) resp, err := conn.Do(req).GetResponse() - testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1010) + selResp, ok := resp.(*SelectResponse) + assert.True(t, ok, "Got wrong response type") + + testConnectionDoSelectRequestCheck(t, selResp, err, true, 2, 1010) + + selPos, err := selResp.Pos() + assert.Nil(t, err, "Error while getting Pos") - resp, err = conn.Do(req.After(resp.Pos())).GetResponse() + resp, err = conn.Do(req.After(selPos)).GetResponse() + selResp, ok = resp.(*SelectResponse) + assert.True(t, ok, "Got wrong response type") - testConnectionDoSelectRequestCheck(t, resp, err, true, 2, 1012) + testConnectionDoSelectRequestCheck(t, selResp, err, true, 2, 1012) } func TestConnection_Call(t *testing.T) { - var resp Response - var err error - conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() - resp, err = conn.Call("simple_concat", []interface{}{"1"}) + data, err := conn.Call("simple_concat", []interface{}{"1"}) if err != nil { t.Errorf("Failed to use Call") } - data, err := resp.Decode() - if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) - } if val, ok := data[0].(string); !ok || val != "11" { t.Errorf("result is not {{1}} : %v", data) } @@ -2678,6 +2700,12 @@ func (req *waitCtxRequest) Async() bool { return NewPingRequest().Async() } +func (req *waitCtxRequest) Response(header Header, body io.Reader) (Response, error) { + resp := BaseResponse{} + resp.SetHeader(header) + return &resp, nil +} + func TestClientRequestObjectsWithContext(t *testing.T) { var err error conn := test_helpers.ConnectWithValidation(t, dialer, opts) diff --git a/test_helpers/main.go b/test_helpers/main.go index 0a0c7f1cd..200c3f474 100644 --- a/test_helpers/main.go +++ b/test_helpers/main.go @@ -83,7 +83,6 @@ type TarantoolInstance struct { func isReady(dialer tarantool.Dialer, opts *tarantool.Opts) error { var err error var conn *tarantool.Connection - var resp tarantool.Response ctx, cancel := GetConnectContext() defer cancel() @@ -96,13 +95,10 @@ func isReady(dialer tarantool.Dialer, opts *tarantool.Opts) error { } defer conn.Close() - resp, err = conn.Do(tarantool.NewPingRequest()).GetResponse() + _, err = conn.Do(tarantool.NewPingRequest()).Get() if err != nil { return err } - if resp == nil { - return errors.New("response is nil after ping") - } return nil } diff --git a/test_helpers/request_mock.go b/test_helpers/request_mock.go index 80ca1b541..980c35db2 100644 --- a/test_helpers/request_mock.go +++ b/test_helpers/request_mock.go @@ -2,6 +2,7 @@ package test_helpers import ( "context" + "io" "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" @@ -35,3 +36,10 @@ func (sr *StrangerRequest) Conn() *tarantool.Connection { func (sr *StrangerRequest) Ctx() context.Context { return nil } + +func (sr *StrangerRequest) Response(header tarantool.Header, + body io.Reader) (tarantool.Response, error) { + resp := tarantool.BaseResponse{} + resp.SetHeader(header) + return &resp, nil +} diff --git a/watch.go b/watch.go index 0508899f0..c147b0399 100644 --- a/watch.go +++ b/watch.go @@ -2,6 +2,7 @@ package tarantool import ( "context" + "io" "github.com/tarantool/go-iproto" "github.com/vmihailenco/msgpack/v5" @@ -55,6 +56,15 @@ func (req *BroadcastRequest) Async() bool { return req.call.Async() } +// Response creates a response for a BroadcastRequest. +func (req *BroadcastRequest) Response(header Header, body io.Reader) (Response, error) { + resp, err := createBaseResponse(header, body) + if err != nil { + return nil, err + } + return &resp, nil +} + // watchRequest subscribes to the updates of a specified key defined on the // server. After receiving the notification, you should send a new // watchRequest to acknowledge the notification. From ebcf54ad3d10fca324e9d6ccf09b3e54d4e8ab2f Mon Sep 17 00:00:00 2001 From: DerekBum Date: Tue, 26 Dec 2023 16:14:24 +0300 Subject: [PATCH 3/4] api: add ability to mock connections in tests Create a mock implementations `MockRequest`, `MockResponse` and `MockDoer`. The last one allows to mock not the full `Connection`, but its part -- a structure, that implements new `Doer` interface (a `Do` function). Also added missing comments, all the changes are recorded in the `CHANGELOG` and `README` files. Added new tests and examples. So this entity is easier to implement and it is enough to mock tests that require working `Connection`. All new mock structs and an example for `MockDoer` usage are added to the `test_helpers` package. Added new structs `MockDoer`, `MockRequest` to `test_helpers`. Renamed `StrangerResponse` to `MockResponse`. Added new connection log constant: `LogAppendPushFailed`. It is logged when connection fails to append a push response. Closes #237 --- CHANGELOG.md | 7 +- README.md | 17 ++- connection.go | 13 +- connector.go | 8 +- const.go | 4 +- dial.go | 35 ++++-- example_test.go | 61 ++++++++- future.go | 12 +- future_test.go | 163 +++++++++++++++++++++--- header.go | 10 +- pool/connection_pool.go | 2 +- pool/connection_pool_test.go | 5 +- prepared.go | 4 +- request.go | 4 +- request_test.go | 160 ++++++++++++++++-------- response.go | 181 +++++++++++++++------------ schema.go | 14 ++- schema_test.go | 162 ++++++++++++++++++++++++ stream.go | 2 +- tarantool_test.go | 235 ++++++++++++++++++++--------------- test_helpers/doer.go | 69 ++++++++++ test_helpers/example_test.go | 36 ++++++ test_helpers/request.go | 52 ++++++++ test_helpers/request_mock.go | 45 ------- test_helpers/response.go | 74 +++++++++++ 25 files changed, 1027 insertions(+), 348 deletions(-) create mode 100644 schema_test.go create mode 100644 test_helpers/doer.go create mode 100644 test_helpers/example_test.go create mode 100644 test_helpers/request.go delete mode 100644 test_helpers/request_mock.go create mode 100644 test_helpers/response.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e15812510..d0f696cc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - `Response` method added to the `Request` interface (#237) - New `LogAppendPushFailed` connection log constant (#237). It is logged when connection fails to append a push response. +- `ErrorNo` constant that indicates that no error has occurred while getting + the response (#237) +- Ability to mock connections for tests (#237). Added new types `MockDoer`, + `MockRequest` to `test_helpers`. ### Changed @@ -88,6 +92,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` and `Pooler` return response data instead of an actual responses (#237) +- Renamed `StrangerResponse` to `MockResponse` (#237) ### Deprecated @@ -110,7 +115,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - IPROTO constants (#158) - Code() method from the Request interface (#158) - `Schema` field from the `Connection` struct (#7) -- `PushCode` constant (#237) +- `OkCode` and `PushCode` constants (#237) ### Fixed diff --git a/README.md b/README.md index 4f82896de..5472e50a2 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,10 @@ The subpackage has been deleted. You could use `pool` instead. * `crud` operations `Timeout` option has `crud.OptFloat64` type instead of `crud.OptUint`. +#### test_helpers package + +Renamed `StrangerResponse` to `MockResponse`. + #### msgpack.v5 Most function names and argument types in `msgpack.v5` and `msgpack.v2` @@ -248,6 +252,8 @@ of the requests is an array instead of array of arrays. * IPROTO constants have been moved to a separate package [go-iproto](https://github.com/tarantool/go-iproto). * `PushCode` constant is removed. To check whether the current response is a push response, use `IsPush()` method of the response iterator instead. +* `ErrorNo` constant is added to indicate that no error has occurred while + getting the response. It should be used instead of the removed `OkCode`. #### Request changes @@ -285,9 +291,10 @@ for an `ops` field. `*Operations` needs to be used instead. #### Connector changes -Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, -`Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` return -response data instead of an actual responses. +* Operations `Ping`, `Select`, `Insert`, `Replace`, `Delete`, `Update`, `Upsert`, + `Call`, `Call16`, `Call17`, `Eval`, `Execute` of a `Connector` return + response data instead of an actual responses. +* New interface `Doer` is added as a child-interface instead of a `Do` method. #### Connect function @@ -304,8 +311,8 @@ for creating a connection are now stored in corresponding `Dialer`, not in `Opts #### Connection schema * Removed `Schema` field from the `Connection` struct. Instead, new -`GetSchema(Connector)` function was added to get the actual connection -schema on demand. + `GetSchema(Doer)` function was added to get the actual connection + schema on demand. * `OverrideSchema(*Schema)` method replaced with the `SetSchema(Schema)`. #### Protocol changes diff --git a/connection.go b/connection.go index 8e3939190..8f8631a31 100644 --- a/connection.go +++ b/connection.go @@ -813,7 +813,7 @@ func (conn *Connection) reader(r io.Reader, c Conn) { return } buf := smallBuf{b: respBytes} - header, err := decodeHeader(conn.dec, &buf) + header, code, err := decodeHeader(conn.dec, &buf) if err != nil { err = ClientError{ ErrProtocolError, @@ -824,7 +824,7 @@ func (conn *Connection) reader(r io.Reader, c Conn) { } var fut *Future = nil - if iproto.Type(header.Code) == iproto.IPROTO_EVENT { + if code == iproto.IPROTO_EVENT { if event, err := readWatchEvent(&buf); err == nil { events <- event } else { @@ -835,7 +835,7 @@ func (conn *Connection) reader(r io.Reader, c Conn) { conn.opts.Logger.Report(LogWatchEventReadFailed, conn, err) } continue - } else if header.Code == uint32(iproto.IPROTO_CHUNK) { + } else if code == iproto.IPROTO_CHUNK { if fut = conn.peekFuture(header.RequestId); fut != nil { if err := fut.AppendPush(header, &buf); err != nil { err = ClientError{ @@ -887,8 +887,7 @@ func (conn *Connection) eventer(events <-chan connWatchEvent) { func (conn *Connection) newFuture(req Request) (fut *Future) { ctx := req.Ctx() - fut = NewFuture() - fut.SetRequest(req) + fut = NewFuture(req) if conn.rlimit != nil && conn.opts.RLimitAction == RLimitDrop { select { case conn.rlimit <- struct{}{}: @@ -1069,7 +1068,7 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) { if fut = conn.fetchFuture(reqid); fut != nil { header := Header{ RequestId: reqid, - Code: OkCode, + Error: ErrorNo, } fut.SetResponse(header, nil) conn.markDone(fut) @@ -1217,7 +1216,7 @@ func (conn *Connection) nextRequestId(context bool) (requestId uint32) { func (conn *Connection) Do(req Request) *Future { if connectedReq, ok := req.(ConnectedRequest); ok { if connectedReq.Conn() != conn { - fut := NewFuture() + fut := NewFuture(req) fut.SetError(errUnknownRequest) return fut } diff --git a/connector.go b/connector.go index 72b5d19a8..9917cff0f 100644 --- a/connector.go +++ b/connector.go @@ -2,14 +2,20 @@ package tarantool import "time" +// Doer is an interface that performs requests asynchronously. +type Doer interface { + // Do performs a request asynchronously. + Do(req Request) (fut *Future) +} + type Connector interface { + Doer ConnectedNow() bool Close() error ConfiguredTimeout() time.Duration NewPrepared(expr string) (*Prepared, error) NewStream() (*Stream, error) NewWatcher(key string, callback WatchCallback) (Watcher, error) - Do(req Request) (fut *Future) // Deprecated: the method will be removed in the next major version, // use a PingRequest object + Do() instead. diff --git a/const.go b/const.go index ede4c988d..e8f389253 100644 --- a/const.go +++ b/const.go @@ -9,5 +9,7 @@ const ( ) const ( - OkCode = uint32(iproto.IPROTO_OK) + // ErrorNo indicates that no error has occurred. It could be used to + // check that a response has an error without the response body decoding. + ErrorNo = iproto.ER_UNKNOWN ) diff --git a/dial.go b/dial.go index 378687925..ff5419760 100644 --- a/dial.go +++ b/dial.go @@ -398,16 +398,17 @@ func identify(w writeFlusher, r io.Reader) (ProtocolInfo, error) { return info, err } - resp, err := readResponse(r) + resp, err := readResponse(r, req) if err != nil { + if resp != nil && + resp.Header().Error == iproto.ER_UNKNOWN_REQUEST_TYPE { + // IPROTO_ID requests are not supported by server. + return info, nil + } return info, err } data, err := resp.Decode() if err != nil { - if iproto.Error(resp.Header().Code) == iproto.ER_UNKNOWN_REQUEST_TYPE { - // IPROTO_ID requests are not supported by server. - return info, nil - } return info, err } @@ -477,7 +478,7 @@ func authenticate(c Conn, auth Auth, user string, pass string, salt string) erro if err = writeRequest(c, req); err != nil { return err } - if _, err = readResponse(c); err != nil { + if _, err = readResponse(c, req); err != nil { return err } return nil @@ -501,19 +502,31 @@ func writeRequest(w writeFlusher, req Request) error { } // readResponse reads a response from the reader. -func readResponse(r io.Reader) (Response, error) { +func readResponse(r io.Reader, req Request) (Response, error) { var lenbuf [packetLengthBytes]byte respBytes, err := read(r, lenbuf[:]) if err != nil { - return &BaseResponse{}, fmt.Errorf("read error: %w", err) + return nil, fmt.Errorf("read error: %w", err) } buf := smallBuf{b: respBytes} - header, err := decodeHeader(msgpack.NewDecoder(&smallBuf{}), &buf) - resp := &BaseResponse{header: header, buf: buf} + header, _, err := decodeHeader(msgpack.NewDecoder(&smallBuf{}), &buf) if err != nil { - return resp, fmt.Errorf("decode response header error: %w", err) + return nil, fmt.Errorf("decode response header error: %w", err) + } + resp, err := req.Response(header, &buf) + if err != nil { + return nil, fmt.Errorf("creating response error: %w", err) + } + _, err = resp.Decode() + if err != nil { + switch err.(type) { + case Error: + return resp, err + default: + return resp, fmt.Errorf("decode response body error: %w", err) + } } return resp, nil } diff --git a/example_test.go b/example_test.go index 3c4d12112..965c25a82 100644 --- a/example_test.go +++ b/example_test.go @@ -198,16 +198,34 @@ func ExampleSelectRequest() { } key := []interface{}{uint(1111)} - data, err := conn.Do(tarantool.NewSelectRequest(617). + resp, err := conn.Do(tarantool.NewSelectRequest(617). Limit(100). Iterator(tarantool.IterEq). Key(key), - ).Get() + ).GetResponse() if err != nil { fmt.Printf("error in select is %v", err) return } + selResp, ok := resp.(*tarantool.SelectResponse) + if !ok { + fmt.Print("wrong response type") + return + } + + pos, err := selResp.Pos() + if err != nil { + fmt.Printf("error in Pos: %v", err) + return + } + fmt.Printf("pos for Select is %v\n", pos) + + data, err := resp.Decode() + if err != nil { + fmt.Printf("error while decoding: %v", err) + return + } fmt.Printf("response is %#v\n", data) var res []Tuple @@ -224,6 +242,7 @@ func ExampleSelectRequest() { fmt.Printf("response is %v\n", res) // Output: + // pos for Select is [] // response is []interface {}{[]interface {}{0x457, "hello", "world"}} // response is [{{} 1111 hello world}] } @@ -567,17 +586,21 @@ func ExampleExecuteRequest() { resp, err := conn.Do(req).GetResponse() fmt.Println("Execute") fmt.Println("Error", err) + data, err := resp.Decode() fmt.Println("Error", err) fmt.Println("Data", data) + exResp, ok := resp.(*tarantool.ExecuteResponse) if !ok { fmt.Printf("wrong response type") return } + metaData, err := exResp.MetaData() fmt.Println("MetaData", metaData) fmt.Println("Error", err) + sqlInfo, err := exResp.SQLInfo() fmt.Println("SQL Info", sqlInfo) fmt.Println("Error", err) @@ -992,6 +1015,26 @@ func ExampleBeginRequest_TxnIsolation() { fmt.Printf("Select after Rollback: response is %#v\n", data) } +func ExampleErrorNo() { + conn := exampleConnect(dialer, opts) + defer conn.Close() + + req := tarantool.NewPingRequest() + resp, err := conn.Do(req).GetResponse() + if err != nil { + fmt.Printf("error getting the response: %s\n", err) + return + } + + if resp.Header().Error != tarantool.ErrorNo { + fmt.Printf("response error code: %s\n", resp.Header().Error) + } else { + fmt.Println("Success.") + } + // Output: + // Success. +} + func ExampleFuture_GetIterator() { conn := exampleConnect(dialer, opts) defer conn.Close() @@ -1008,11 +1051,11 @@ func ExampleFuture_GetIterator() { if it.IsPush() { // It is a push message. fmt.Printf("push message: %v\n", data[0]) - } else if resp.Header().Code == tarantool.OkCode { + } else if resp.Header().Error == tarantool.ErrorNo { // It is a regular response. fmt.Printf("response: %v", data[0]) } else { - fmt.Printf("an unexpected response code %d", resp.Header().Code) + fmt.Printf("an unexpected response code %d", resp.Header().Error) } } if err := it.Err(); err != nil { @@ -1224,6 +1267,11 @@ func ExampleConnection_Do_failure() { if err != nil { fmt.Printf("Error in the future: %s\n", err) } + // Optional step: check a response error. + // It allows checking that response has or hasn't an error without decoding. + if resp.Header().Error != tarantool.ErrorNo { + fmt.Printf("Response error: %s\n", resp.Header().Error) + } data, err := future.Get() if err != nil { @@ -1239,8 +1287,8 @@ func ExampleConnection_Do_failure() { } else { // Response exist. So it could be a Tarantool error or a decode // error. We need to check the error code. - fmt.Printf("Error code from the response: %d\n", resp.Header().Code) - if resp.Header().Code == tarantool.OkCode { + fmt.Printf("Error code from the response: %d\n", resp.Header().Error) + if resp.Header().Error == tarantool.ErrorNo { fmt.Printf("Decode error: %s\n", err) } else { code := err.(tarantool.Error).Code @@ -1251,6 +1299,7 @@ func ExampleConnection_Do_failure() { } // Output: + // Response error: ER_NO_SUCH_PROC // Data: [] // Error code from the response: 33 // Error code from the error: 33 diff --git a/future.go b/future.go index 139782637..1b9f2ed14 100644 --- a/future.go +++ b/future.go @@ -121,7 +121,7 @@ func (it *asyncResponseIterator) nextResponse() (resp Response) { // PushResponse is used for push requests for the Future. type PushResponse struct { - BaseResponse + baseResponse } func createPushResponse(header Header, body io.Reader) (Response, error) { @@ -132,12 +132,13 @@ func createPushResponse(header Header, body io.Reader) (Response, error) { return &PushResponse{resp}, nil } -// NewFuture creates a new empty Future. -func NewFuture() (fut *Future) { +// NewFuture creates a new empty Future for a given Request. +func NewFuture(req Request) (fut *Future) { fut = &Future{} fut.ready = make(chan struct{}, 1000000000) fut.done = make(chan struct{}) fut.pushes = make([]Response, 0) + fut.req = req return fut } @@ -163,11 +164,6 @@ func (fut *Future) AppendPush(header Header, body io.Reader) error { return nil } -// SetRequest sets a request, for which the future was created. -func (fut *Future) SetRequest(req Request) { - fut.req = req -} - // SetResponse sets a response for the future and finishes the future. func (fut *Future) SetResponse(header Header, body io.Reader) error { fut.mutex.Lock() diff --git a/future_test.go b/future_test.go index 947e4ed28..6efda10a1 100644 --- a/future_test.go +++ b/future_test.go @@ -1,15 +1,86 @@ package tarantool_test import ( + "bytes" + "context" "errors" + "io" + "io/ioutil" "sync" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/tarantool/go-iproto" . "github.com/tarantool/go-tarantool/v2" + "github.com/tarantool/go-tarantool/v2/test_helpers" + "github.com/vmihailenco/msgpack/v5" ) +type futureMockRequest struct { +} + +func (req *futureMockRequest) Type() iproto.Type { + return iproto.Type(0) +} + +func (req *futureMockRequest) Async() bool { + return false +} + +func (req *futureMockRequest) Body(resolver SchemaResolver, enc *msgpack.Encoder) error { + return nil +} + +func (req *futureMockRequest) Conn() *Connection { + return &Connection{} +} + +func (req *futureMockRequest) Ctx() context.Context { + return nil +} + +func (req *futureMockRequest) Response(header Header, + body io.Reader) (Response, error) { + resp, err := createFutureMockResponse(header, body) + return resp, err +} + +type futureMockResponse struct { + header Header + data []byte + + decodeCnt int + decodeTypedCnt int +} + +func (resp *futureMockResponse) Header() Header { + return resp.header +} + +func (resp *futureMockResponse) Decode() ([]interface{}, error) { + resp.decodeCnt++ + + dataInt := make([]interface{}, len(resp.data)) + for i := range resp.data { + dataInt[i] = resp.data[i] + } + return dataInt, nil +} + +func (resp *futureMockResponse) DecodeTyped(res interface{}) error { + resp.decodeTypedCnt++ + return nil +} + +func createFutureMockResponse(header Header, body io.Reader) (Response, error) { + data, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + return &futureMockResponse{header: header, data: data}, nil +} + func assertResponseIteratorValue(t testing.TB, it ResponseIterator, isPush bool, resp Response) { t.Helper() @@ -43,7 +114,7 @@ func assertResponseIteratorFinished(t testing.TB, it ResponseIterator) { } func TestFutureGetIteratorNoItems(t *testing.T) { - fut := NewFuture() + fut := NewFuture(test_helpers.NewMockRequest()) it := fut.GetIterator() if it.Next() { @@ -56,7 +127,7 @@ func TestFutureGetIteratorNoItems(t *testing.T) { func TestFutureGetIteratorNoResponse(t *testing.T) { pushHeader := Header{} push := &PushResponse{} - fut := NewFuture() + fut := NewFuture(test_helpers.NewMockRequest()) fut.AppendPush(pushHeader, nil) if it := fut.GetIterator(); it.Next() { @@ -73,7 +144,7 @@ func TestFutureGetIteratorNoResponse(t *testing.T) { func TestFutureGetIteratorNoResponseTimeout(t *testing.T) { pushHeader := Header{} push := &PushResponse{} - fut := NewFuture() + fut := NewFuture(test_helpers.NewMockRequest()) fut.AppendPush(pushHeader, nil) if it := fut.GetIterator().WithTimeout(1 * time.Nanosecond); it.Next() { @@ -91,8 +162,8 @@ func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { pushHeader := Header{} respHeader := Header{} push := &PushResponse{} - resp := &BaseResponse{} - fut := NewFuture() + resp := &test_helpers.MockResponse{} + fut := NewFuture(test_helpers.NewMockRequest()) fut.AppendPush(pushHeader, nil) var done sync.WaitGroup @@ -128,15 +199,13 @@ func TestFutureGetIteratorResponseOnTimeout(t *testing.T) { wait.Wait() - fut.SetRequest(&InsertRequest{}) fut.SetResponse(respHeader, nil) done.Wait() } func TestFutureGetIteratorFirstResponse(t *testing.T) { - resp := &BaseResponse{} - fut := NewFuture() - fut.SetRequest(&InsertRequest{}) + resp := &test_helpers.MockResponse{} + fut := NewFuture(test_helpers.NewMockRequest()) fut.SetResponse(Header{}, nil) fut.SetResponse(Header{}, nil) @@ -155,7 +224,7 @@ func TestFutureGetIteratorFirstError(t *testing.T) { const errMsg1 = "error1" const errMsg2 = "error2" - fut := NewFuture() + fut := NewFuture(test_helpers.NewMockRequest()) fut.SetError(errors.New(errMsg1)) fut.SetError(errors.New(errMsg2)) @@ -173,11 +242,10 @@ func TestFutureGetIteratorResponse(t *testing.T) { responses := []Response{ &PushResponse{}, &PushResponse{}, - &BaseResponse{}, + &test_helpers.MockResponse{}, } header := Header{} - fut := NewFuture() - fut.SetRequest(&InsertRequest{}) + fut := NewFuture(test_helpers.NewMockRequest()) for i := range responses { if i == len(responses)-1 { fut.SetResponse(header, nil) @@ -215,7 +283,7 @@ func TestFutureGetIteratorError(t *testing.T) { {}, } err := errors.New(errMsg) - fut := NewFuture() + fut := NewFuture(test_helpers.NewMockRequest()) for range responses { fut.AppendPush(Header{}, nil) } @@ -249,8 +317,7 @@ func TestFutureSetStateRaceCondition(t *testing.T) { err := errors.New("any error") for i := 0; i < 1000; i++ { - fut := NewFuture() - fut.SetRequest(&InsertRequest{}) + fut := NewFuture(test_helpers.NewMockRequest()) for j := 0; j < 9; j++ { go func(opt int) { if opt%3 == 0 { @@ -266,3 +333,67 @@ func TestFutureSetStateRaceCondition(t *testing.T) { // It may be false-positive, but very rarely - it's ok for such very // simple race conditions tests. } + +func TestFutureGetIteratorIsPush(t *testing.T) { + fut := NewFuture(test_helpers.NewMockRequest()) + fut.AppendPush(Header{}, nil) + fut.SetResponse(Header{}, nil) + it := fut.GetIterator() + + it.Next() + assert.True(t, it.IsPush()) + it.Next() + assert.False(t, it.IsPush()) +} + +func TestFuture_Get(t *testing.T) { + fut := NewFuture(&futureMockRequest{}) + fut.SetResponse(Header{}, bytes.NewReader([]byte{'v', '2'})) + + resp, err := fut.GetResponse() + assert.NoError(t, err) + mockResp, ok := resp.(*futureMockResponse) + assert.True(t, ok) + + data, err := fut.Get() + assert.NoError(t, err) + assert.Equal(t, []interface{}{uint8('v'), uint8('2')}, data) + assert.Equal(t, 1, mockResp.decodeCnt) + assert.Equal(t, 0, mockResp.decodeTypedCnt) +} + +func TestFuture_GetTyped(t *testing.T) { + fut := NewFuture(&futureMockRequest{}) + fut.SetResponse(Header{}, bytes.NewReader([]byte{'v', '2'})) + + resp, err := fut.GetResponse() + assert.NoError(t, err) + mockResp, ok := resp.(*futureMockResponse) + assert.True(t, ok) + + var data []byte + + err = fut.GetTyped(&data) + assert.NoError(t, err) + assert.Equal(t, 0, mockResp.decodeCnt) + assert.Equal(t, 1, mockResp.decodeTypedCnt) +} + +func TestFuture_GetResponse(t *testing.T) { + mockResp, err := createFutureMockResponse(Header{}, + bytes.NewReader([]byte{'v', '2'})) + assert.NoError(t, err) + + fut := NewFuture(&futureMockRequest{}) + fut.SetResponse(Header{}, bytes.NewReader([]byte{'v', '2'})) + + resp, err := fut.GetResponse() + assert.NoError(t, err) + respConv, ok := resp.(*futureMockResponse) + assert.True(t, ok) + assert.Equal(t, mockResp, respConv) + + data, err := resp.Decode() + assert.NoError(t, err) + assert.Equal(t, []interface{}{uint8('v'), uint8('2')}, data) +} diff --git a/header.go b/header.go index d9069c23a..20a4a465f 100644 --- a/header.go +++ b/header.go @@ -1,10 +1,14 @@ package tarantool +import "github.com/tarantool/go-iproto" + // Header is a response header. type Header struct { // RequestId is an id of a corresponding request. RequestId uint32 - // Code is a response code. It could be used to check that response - // has or hasn't an error. - Code uint32 + // Error is a response error. It could be used + // to check that response has or hasn't an error without decoding. + // Error == ErrorNo (iproto.ER_UNKNOWN) if there is no error. + // Otherwise, it contains an error code from iproto.Error enumeration. + Error iproto.Error } diff --git a/pool/connection_pool.go b/pool/connection_pool.go index 3734c4c0a..861221290 100644 --- a/pool/connection_pool.go +++ b/pool/connection_pool.go @@ -1460,7 +1460,7 @@ func (p *ConnectionPool) getConnByMode(defaultMode Mode, } func newErrorFuture(err error) *tarantool.Future { - fut := tarantool.NewFuture() + fut := tarantool.NewFuture(nil) fut.SetError(err) return fut } diff --git a/pool/connection_pool_test.go b/pool/connection_pool_test.go index ae1acd223..acdc756d3 100644 --- a/pool/connection_pool_test.go +++ b/pool/connection_pool_test.go @@ -2379,9 +2379,8 @@ func TestPing(t *testing.T) { require.Nilf(t, data, "response data is not nil after Ping") // RO - data, err = connPool.Ping(pool.RO) + _, err = connPool.Ping(pool.RO) require.Nilf(t, err, "failed to Ping") - require.Nilf(t, data, "response data is not nil after Ping") // PreferRW data, err = connPool.Ping(pool.PreferRW) @@ -2549,7 +2548,7 @@ func TestDoWithStrangerConn(t *testing.T) { defer connPool.Close() - req := test_helpers.NewStrangerRequest() + req := test_helpers.NewMockRequest() _, err = connPool.Do(req, pool.ANY).Get() if err == nil { diff --git a/prepared.go b/prepared.go index 35b73272e..3a03d740f 100644 --- a/prepared.go +++ b/prepared.go @@ -102,7 +102,7 @@ func (req *PrepareRequest) Response(header Header, body io.Reader) (Response, er if err != nil { return nil, err } - return &PrepareResponse{BaseResponse: baseResp}, nil + return &PrepareResponse{baseResponse: baseResp}, nil } // UnprepareRequest helps you to create an unprepare request object for @@ -192,5 +192,5 @@ func (req *ExecutePreparedRequest) Response(header Header, body io.Reader) (Resp if err != nil { return nil, err } - return &ExecuteResponse{BaseResponse: baseResp}, nil + return &ExecuteResponse{baseResponse: baseResp}, nil } diff --git a/request.go b/request.go index 716e5ab2c..8dbe250b5 100644 --- a/request.go +++ b/request.go @@ -1109,7 +1109,7 @@ func (req *SelectRequest) Response(header Header, body io.Reader) (Response, err if err != nil { return nil, err } - return &SelectResponse{BaseResponse: baseResp}, nil + return &SelectResponse{baseResponse: baseResp}, nil } // InsertRequest helps you to create an insert request object for execution @@ -1517,7 +1517,7 @@ func (req *ExecuteRequest) Response(header Header, body io.Reader) (Response, er if err != nil { return nil, err } - return &ExecuteResponse{BaseResponse: baseResp}, nil + return &ExecuteResponse{baseResponse: baseResp}, nil } // WatchOnceRequest synchronously fetches the value currently associated with a diff --git a/request_test.go b/request_test.go index 580259702..84ba23ef6 100644 --- a/request_test.go +++ b/request_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" "testing" "time" @@ -317,58 +318,6 @@ 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 @@ -1007,3 +956,110 @@ func TestWatchOnceRequestDefaultValues(t *testing.T) { req := NewWatchOnceRequest(validKey) assertBodyEqual(t, refBuf.Bytes(), req) } + +func TestResponseDecode(t *testing.T) { + header := Header{} + data := bytes.NewBuffer([]byte{'v', '2'}) + baseExample, err := NewPingRequest().Response(header, data) + assert.NoError(t, err) + + tests := []struct { + req Request + expected Response + }{ + {req: NewSelectRequest(validSpace), expected: &SelectResponse{}}, + {req: NewUpdateRequest(validSpace), expected: baseExample}, + {req: NewUpsertRequest(validSpace), expected: baseExample}, + {req: NewInsertRequest(validSpace), expected: baseExample}, + {req: NewReplaceRequest(validSpace), expected: baseExample}, + {req: NewDeleteRequest(validSpace), expected: baseExample}, + {req: NewCallRequest(validExpr), expected: baseExample}, + {req: NewCall16Request(validExpr), expected: baseExample}, + {req: NewCall17Request(validExpr), expected: baseExample}, + {req: NewEvalRequest(validExpr), expected: baseExample}, + {req: NewExecuteRequest(validExpr), expected: &ExecuteResponse{}}, + {req: NewPingRequest(), expected: baseExample}, + {req: NewPrepareRequest(validExpr), expected: &PrepareResponse{}}, + {req: NewUnprepareRequest(validStmt), expected: baseExample}, + {req: NewExecutePreparedRequest(validStmt), expected: &ExecuteResponse{}}, + {req: NewBeginRequest(), expected: baseExample}, + {req: NewCommitRequest(), expected: baseExample}, + {req: NewRollbackRequest(), expected: baseExample}, + {req: NewIdRequest(validProtocolInfo), expected: baseExample}, + {req: NewBroadcastRequest(validKey), expected: baseExample}, + {req: NewWatchOnceRequest(validKey), expected: baseExample}, + } + + for _, test := range tests { + buf := bytes.NewBuffer([]byte{}) + enc := msgpack.NewEncoder(buf) + + enc.EncodeMapLen(1) + enc.EncodeUint8(uint8(iproto.IPROTO_DATA)) + enc.Encode([]interface{}{'v', '2'}) + + resp, err := test.req.Response(header, bytes.NewBuffer(buf.Bytes())) + assert.NoError(t, err) + assert.True(t, fmt.Sprintf("%T", resp) == + fmt.Sprintf("%T", test.expected)) + assert.Equal(t, header, resp.Header()) + + decodedInterface, err := resp.Decode() + assert.NoError(t, err) + assert.Equal(t, []interface{}{'v', '2'}, decodedInterface) + } +} + +func TestResponseDecodeTyped(t *testing.T) { + header := Header{} + data := bytes.NewBuffer([]byte{'v', '2'}) + baseExample, err := NewPingRequest().Response(header, data) + assert.NoError(t, err) + + tests := []struct { + req Request + expected Response + }{ + {req: NewSelectRequest(validSpace), expected: &SelectResponse{}}, + {req: NewUpdateRequest(validSpace), expected: baseExample}, + {req: NewUpsertRequest(validSpace), expected: baseExample}, + {req: NewInsertRequest(validSpace), expected: baseExample}, + {req: NewReplaceRequest(validSpace), expected: baseExample}, + {req: NewDeleteRequest(validSpace), expected: baseExample}, + {req: NewCallRequest(validExpr), expected: baseExample}, + {req: NewCall16Request(validExpr), expected: baseExample}, + {req: NewCall17Request(validExpr), expected: baseExample}, + {req: NewEvalRequest(validExpr), expected: baseExample}, + {req: NewExecuteRequest(validExpr), expected: &ExecuteResponse{}}, + {req: NewPingRequest(), expected: baseExample}, + {req: NewPrepareRequest(validExpr), expected: &PrepareResponse{}}, + {req: NewUnprepareRequest(validStmt), expected: baseExample}, + {req: NewExecutePreparedRequest(validStmt), expected: &ExecuteResponse{}}, + {req: NewBeginRequest(), expected: baseExample}, + {req: NewCommitRequest(), expected: baseExample}, + {req: NewRollbackRequest(), expected: baseExample}, + {req: NewIdRequest(validProtocolInfo), expected: baseExample}, + {req: NewBroadcastRequest(validKey), expected: baseExample}, + {req: NewWatchOnceRequest(validKey), expected: baseExample}, + } + + for _, test := range tests { + buf := bytes.NewBuffer([]byte{}) + enc := msgpack.NewEncoder(buf) + + enc.EncodeMapLen(1) + enc.EncodeUint8(uint8(iproto.IPROTO_DATA)) + enc.EncodeBytes([]byte{'v', '2'}) + + resp, err := test.req.Response(header, bytes.NewBuffer(buf.Bytes())) + assert.NoError(t, err) + assert.True(t, fmt.Sprintf("%T", resp) == + fmt.Sprintf("%T", test.expected)) + assert.Equal(t, header, resp.Header()) + + var decoded []byte + err = resp.DecodeTyped(&decoded) + assert.NoError(t, err) + assert.Equal(t, []byte{'v', '2'}, decoded) + } +} diff --git a/response.go b/response.go index 61c77f413..db88c743c 100644 --- a/response.go +++ b/response.go @@ -19,32 +19,31 @@ type Response interface { DecodeTyped(res interface{}) error } -// BaseResponse is a base Response interface implementation. -type BaseResponse struct { +type baseResponse struct { + // header is a response header. header Header // data contains deserialized data for untyped requests. - data []interface{} - buf smallBuf - decoded bool + data []interface{} + buf smallBuf + // Was the Decode() func called for this response. + decoded bool + // Was the DecodeTyped() func called for this response. decodedTyped bool + err error } -func createBaseResponse(header Header, body io.Reader) (BaseResponse, error) { +func createBaseResponse(header Header, body io.Reader) (baseResponse, error) { if body == nil { - return BaseResponse{header: header}, nil + return baseResponse{header: header}, nil } if buf, ok := body.(*smallBuf); ok { - return BaseResponse{header: header, buf: *buf}, nil + return baseResponse{header: header, buf: *buf}, nil } data, err := ioutil.ReadAll(body) if err != nil { - return BaseResponse{}, err + return baseResponse{}, err } - return BaseResponse{header: header, buf: smallBuf{b: data}}, nil -} - -func (resp *BaseResponse) SetHeader(header Header) { - resp.header = header + return baseResponse{header: header, buf: smallBuf{b: data}}, nil } // SelectResponse is used for the select requests. @@ -52,7 +51,7 @@ func (resp *BaseResponse) SetHeader(header Header) { // // You need to cast to SelectResponse a response from SelectRequest. type SelectResponse struct { - BaseResponse + baseResponse // pos contains a position descriptor of last selected tuple. pos []byte } @@ -70,7 +69,7 @@ type PrepareResponse ExecuteResponse // // You need to cast to ExecuteResponse a response from ExecuteRequest. type ExecuteResponse struct { - BaseResponse + baseResponse metaData []ColumnMetaData sqlInfo SQLInfo } @@ -169,39 +168,43 @@ func smallInt(d *msgpack.Decoder, buf *smallBuf) (i int, err error) { return d.DecodeInt() } -func decodeHeader(d *msgpack.Decoder, buf *smallBuf) (Header, error) { +func decodeHeader(d *msgpack.Decoder, buf *smallBuf) (Header, iproto.Type, error) { var l int + var code int var err error d.Reset(buf) if l, err = d.DecodeMapLen(); err != nil { - return Header{}, err + return Header{}, 0, err } - decodedHeader := Header{} + decodedHeader := Header{Error: ErrorNo} for ; l > 0; l-- { var cd int if cd, err = smallInt(d, buf); err != nil { - return Header{}, err + return Header{}, 0, err } switch iproto.Key(cd) { case iproto.IPROTO_SYNC: var rid uint64 if rid, err = d.DecodeUint64(); err != nil { - return Header{}, err + return Header{}, 0, err } decodedHeader.RequestId = uint32(rid) case iproto.IPROTO_REQUEST_TYPE: - var rcode uint64 - if rcode, err = d.DecodeUint64(); err != nil { - return Header{}, err + if code, err = d.DecodeInt(); err != nil { + return Header{}, 0, err + } + if code&int(iproto.IPROTO_TYPE_ERROR) != 0 { + decodedHeader.Error = iproto.Error(code &^ int(iproto.IPROTO_TYPE_ERROR)) + } else { + decodedHeader.Error = ErrorNo } - decodedHeader.Code = uint32(rcode) default: if err = d.Skip(); err != nil { - return Header{}, err + return Header{}, 0, err } } } - return decodedHeader, nil + return decodedHeader, iproto.Type(code), nil } type decodeInfo struct { @@ -213,7 +216,7 @@ type decodeInfo struct { decodedError string } -func (info *decodeInfo) parseData(resp *BaseResponse) error { +func (info *decodeInfo) parseData(resp *baseResponse) error { if info.stmtID != 0 { stmt := &Prepared{ StatementID: PreparedID(info.stmtID), @@ -307,7 +310,11 @@ func decodeCommonField(d *msgpack.Decoder, cd int, data *[]interface{}, return true, nil } -func (resp *BaseResponse) Decode() ([]interface{}, error) { +func (resp *baseResponse) Decode() ([]interface{}, error) { + if resp.decoded { + return resp.data, resp.err + } + resp.decoded = true var err error if resp.buf.Len() > 2 { @@ -323,37 +330,46 @@ func (resp *BaseResponse) Decode() ([]interface{}, error) { }) if l, err = d.DecodeMapLen(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } for ; l > 0; l-- { var cd int if cd, err = smallInt(d, &resp.buf); err != nil { - return nil, err + resp.err = err + return nil, resp.err } decoded, err := decodeCommonField(d, cd, &resp.data, info) if err != nil { - return nil, err + resp.err = err + return nil, resp.err } if !decoded { if err = d.Skip(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } } } err = info.parseData(resp) if err != nil { - return nil, err + resp.err = err + return nil, resp.err } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + resp.err = Error{resp.header.Error, info.decodedError, + info.errorExtendedInfo} } } - return resp.data, err + return resp.data, resp.err } func (resp *SelectResponse) Decode() ([]interface{}, error) { + if resp.decoded { + return resp.data, resp.err + } + resp.decoded = true var err error if resp.buf.Len() > 2 { @@ -369,44 +385,54 @@ func (resp *SelectResponse) Decode() ([]interface{}, error) { }) if l, err = d.DecodeMapLen(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } for ; l > 0; l-- { var cd int if cd, err = smallInt(d, &resp.buf); err != nil { - return nil, err + resp.err = err + return nil, resp.err } decoded, err := decodeCommonField(d, cd, &resp.data, info) if err != nil { + resp.err = err return nil, err } if !decoded { switch iproto.Key(cd) { case iproto.IPROTO_POSITION: if resp.pos, err = d.DecodeBytes(); err != nil { - return nil, fmt.Errorf("unable to decode a position: %w", err) + resp.err = err + return nil, fmt.Errorf("unable to decode a position: %w", resp.err) } default: if err = d.Skip(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } } } } - err = info.parseData(&resp.BaseResponse) + err = info.parseData(&resp.baseResponse) if err != nil { - return nil, err + resp.err = err + return nil, resp.err } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + resp.err = Error{resp.header.Error, info.decodedError, + info.errorExtendedInfo} } } - return resp.data, err + return resp.data, resp.err } func (resp *ExecuteResponse) Decode() ([]interface{}, error) { + if resp.decoded { + return resp.data, resp.err + } + resp.decoded = true var err error if resp.buf.Len() > 2 { @@ -422,45 +448,52 @@ func (resp *ExecuteResponse) Decode() ([]interface{}, error) { }) if l, err = d.DecodeMapLen(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } for ; l > 0; l-- { var cd int if cd, err = smallInt(d, &resp.buf); err != nil { - return nil, err + resp.err = err + return nil, resp.err } decoded, err := decodeCommonField(d, cd, &resp.data, info) if err != nil { - return nil, err + resp.err = err + return nil, resp.err } if !decoded { switch iproto.Key(cd) { case iproto.IPROTO_SQL_INFO: if err = d.Decode(&resp.sqlInfo); err != nil { - return nil, err + resp.err = err + return nil, resp.err } case iproto.IPROTO_METADATA: if err = d.Decode(&resp.metaData); err != nil { - return nil, err + resp.err = err + return nil, resp.err } default: if err = d.Skip(); err != nil { - return nil, err + resp.err = err + return nil, resp.err } } } } - err = info.parseData(&resp.BaseResponse) + err = info.parseData(&resp.baseResponse) if err != nil { - return nil, err + resp.err = err + return nil, resp.err } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + resp.err = Error{resp.header.Error, info.decodedError, + info.errorExtendedInfo} } } - return resp.data, err + return resp.data, resp.err } func decodeTypedCommonField(d *msgpack.Decoder, res interface{}, cd int, @@ -486,7 +519,7 @@ func decodeTypedCommonField(d *msgpack.Decoder, res interface{}, cd int, return true, nil } -func (resp *BaseResponse) DecodeTyped(res interface{}) error { +func (resp *baseResponse) DecodeTyped(res interface{}) error { resp.decodedTyped = true var err error @@ -521,8 +554,7 @@ func (resp *BaseResponse) DecodeTyped(res interface{}) error { } } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + err = Error{resp.header.Error, info.decodedError, info.errorExtendedInfo} } } return err @@ -570,8 +602,7 @@ func (resp *SelectResponse) DecodeTyped(res interface{}) error { } } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + err = Error{resp.header.Error, info.decodedError, info.errorExtendedInfo} } } return err @@ -623,51 +654,47 @@ func (resp *ExecuteResponse) DecodeTyped(res interface{}) error { } } if info.decodedError != "" { - resp.header.Code &^= uint32(iproto.IPROTO_TYPE_ERROR) - err = Error{iproto.Error(resp.header.Code), info.decodedError, info.errorExtendedInfo} + err = Error{resp.header.Error, info.decodedError, info.errorExtendedInfo} } } return err } -func (resp *BaseResponse) Header() Header { +func (resp *baseResponse) Header() Header { return resp.header } // Pos returns a position descriptor of the last selected tuple for the SelectResponse. // If the response was not decoded, this method will call Decode(). func (resp *SelectResponse) Pos() ([]byte, error) { - var err error if !resp.decoded && !resp.decodedTyped { - _, err = resp.Decode() + resp.Decode() } - return resp.pos, err + return resp.pos, resp.err } // MetaData returns ExecuteResponse meta-data. // If the response was not decoded, this method will call Decode(). func (resp *ExecuteResponse) MetaData() ([]ColumnMetaData, error) { - var err error if !resp.decoded && !resp.decodedTyped { - _, err = resp.Decode() + resp.Decode() } - return resp.metaData, err + return resp.metaData, resp.err } // SQLInfo returns ExecuteResponse sql info. // If the response was not decoded, this method will call Decode(). func (resp *ExecuteResponse) SQLInfo() (SQLInfo, error) { - var err error if !resp.decoded && !resp.decodedTyped { - _, err = resp.Decode() + resp.Decode() } - return resp.sqlInfo, err + return resp.sqlInfo, resp.err } // String implements Stringer interface. -func (resp *BaseResponse) String() (str string) { - if resp.header.Code == OkCode { +func (resp *baseResponse) String() (str string) { + if resp.header.Error == ErrorNo { return fmt.Sprintf("<%d OK %v>", resp.header.RequestId, resp.data) } - return fmt.Sprintf("<%d ERR 0x%x>", resp.header.RequestId, resp.header.Code) + return fmt.Sprintf("<%d ERR %s %v>", resp.header.RequestId, resp.header.Error, resp.err) } diff --git a/schema.go b/schema.go index 6066adf49..72b5e397f 100644 --- a/schema.go +++ b/schema.go @@ -380,15 +380,18 @@ func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error { return errors.New("unexpected schema format (index fields)") } -// GetSchema returns the actual schema for the connection. -func GetSchema(conn Connector) (Schema, error) { +// GetSchema returns the actual schema for the Doer. +func GetSchema(doer Doer) (Schema, error) { schema := Schema{} schema.SpacesById = make(map[uint32]Space) schema.Spaces = make(map[string]Space) // Reload spaces. var spaces []Space - err := conn.SelectTyped(vspaceSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &spaces) + req := NewSelectRequest(vspaceSpId). + Index(0). + Limit(maxSchemas) + err := doer.Do(req).GetTyped(&spaces) if err != nil { return Schema{}, err } @@ -399,7 +402,10 @@ func GetSchema(conn Connector) (Schema, error) { // Reload indexes. var indexes []Index - err = conn.SelectTyped(vindexSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &indexes) + req = NewSelectRequest(vindexSpId). + Index(0). + Limit(maxSchemas) + err = doer.Do(req).GetTyped(&indexes) if err != nil { return Schema{}, err } diff --git a/schema_test.go b/schema_test.go new file mode 100644 index 000000000..631591cb2 --- /dev/null +++ b/schema_test.go @@ -0,0 +1,162 @@ +package tarantool_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-tarantool/v2" + "github.com/tarantool/go-tarantool/v2/test_helpers" +) + +func TestGetSchema_ok(t *testing.T) { + space1 := tarantool.Space{ + Id: 1, + Name: "name1", + Indexes: make(map[string]tarantool.Index), + IndexesById: make(map[uint32]tarantool.Index), + Fields: make(map[string]tarantool.Field), + FieldsById: make(map[uint32]tarantool.Field), + } + index := tarantool.Index{ + Id: 1, + SpaceId: 2, + Name: "index_name", + Type: "index_type", + Unique: true, + Fields: make([]tarantool.IndexField, 0), + } + space2 := tarantool.Space{ + Id: 2, + Name: "name2", + Indexes: map[string]tarantool.Index{ + "index_name": index, + }, + IndexesById: map[uint32]tarantool.Index{ + 1: index, + }, + Fields: make(map[string]tarantool.Field), + FieldsById: make(map[uint32]tarantool.Field), + } + + mockDoer := test_helpers.NewMockDoer(t, + test_helpers.NewMockResponse(t, [][]interface{}{ + { + uint32(1), + "skip", + "name1", + "", + 0, + }, + { + uint32(2), + "skip", + "name2", + "", + 0, + }, + }), + test_helpers.NewMockResponse(t, [][]interface{}{ + { + uint32(2), + uint32(1), + "index_name", + "index_type", + uint8(1), + uint8(0), + }, + }), + ) + + expectedSchema := tarantool.Schema{ + SpacesById: map[uint32]tarantool.Space{ + 1: space1, + 2: space2, + }, + Spaces: map[string]tarantool.Space{ + "name1": space1, + "name2": space2, + }, + } + + schema, err := tarantool.GetSchema(&mockDoer) + require.NoError(t, err) + require.Equal(t, expectedSchema, schema) +} + +func TestGetSchema_spaces_select_error(t *testing.T) { + mockDoer := test_helpers.NewMockDoer(t, fmt.Errorf("some error")) + + schema, err := tarantool.GetSchema(&mockDoer) + require.EqualError(t, err, "some error") + require.Equal(t, tarantool.Schema{}, schema) +} + +func TestGetSchema_index_select_error(t *testing.T) { + mockDoer := test_helpers.NewMockDoer(t, + test_helpers.NewMockResponse(t, [][]interface{}{ + { + uint32(1), + "skip", + "name1", + "", + 0, + }, + }), + fmt.Errorf("some error")) + + schema, err := tarantool.GetSchema(&mockDoer) + require.EqualError(t, err, "some error") + require.Equal(t, tarantool.Schema{}, schema) +} + +func TestResolverCalledWithoutNameSupport(t *testing.T) { + resolver := ValidSchemeResolver{nameUseSupported: false} + + req := tarantool.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 := ValidSchemeResolver{nameUseSupported: true} + + req := tarantool.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) + } +} diff --git a/stream.go b/stream.go index 5144ea6f1..43e80fc28 100644 --- a/stream.go +++ b/stream.go @@ -199,7 +199,7 @@ func (req *RollbackRequest) Context(ctx context.Context) *RollbackRequest { func (s *Stream) Do(req Request) *Future { if connectedReq, ok := req.(ConnectedRequest); ok { if connectedReq.Conn() != s.Conn { - fut := NewFuture() + fut := NewFuture(req) fut.SetError(errUnknownStreamRequest) return fut } diff --git a/tarantool_test.go b/tarantool_test.go index 3b8bc4653..c3f6b4c0b 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -858,7 +858,7 @@ func TestClient(t *testing.T) { // Ping data, err := conn.Ping() if err != nil { - t.Fatalf("Failed to Ping: %s", err.Error()) + t.Fatalf("Failed to Ping: %s", err) } if data != nil { t.Fatalf("Response data is not nil after Ping") @@ -867,7 +867,7 @@ func TestClient(t *testing.T) { // Insert data, err = conn.Insert(spaceNo, []interface{}{uint(1), "hello", "world"}) if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } if len(data) != 1 { t.Errorf("Response Body len != 1") @@ -896,7 +896,7 @@ func TestClient(t *testing.T) { // Delete data, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(1)}) if err != nil { - t.Fatalf("Failed to Delete: %s", err.Error()) + t.Fatalf("Failed to Delete: %s", err) } if len(data) != 1 { t.Errorf("Response Body len != 1") @@ -916,7 +916,7 @@ func TestClient(t *testing.T) { } data, err = conn.Delete(spaceNo, indexNo, []interface{}{uint(101)}) if err != nil { - t.Fatalf("Failed to Delete: %s", err.Error()) + t.Fatalf("Failed to Delete: %s", err) } if len(data) != 0 { t.Errorf("Response Data len != 0") @@ -925,14 +925,14 @@ func TestClient(t *testing.T) { // Replace data, err = conn.Replace(spaceNo, []interface{}{uint(2), "hello", "world"}) if err != nil { - t.Fatalf("Failed to Replace: %s", err.Error()) + t.Fatalf("Failed to Replace: %s", err) } if data == nil { t.Fatalf("Response is nil after Replace") } data, err = conn.Replace(spaceNo, []interface{}{uint(2), "hi", "planet"}) if err != nil { - t.Fatalf("Failed to Replace (duplicate): %s", err.Error()) + t.Fatalf("Failed to Replace (duplicate): %s", err) } if len(data) != 1 { t.Errorf("Response Data len != 1") @@ -955,7 +955,7 @@ func TestClient(t *testing.T) { data, err = conn.Update(spaceNo, indexNo, []interface{}{uint(2)}, NewOperations().Assign(1, "bye").Delete(2, 1)) if err != nil { - t.Fatalf("Failed to Update: %s", err.Error()) + t.Fatalf("Failed to Update: %s", err) } if len(data) != 1 { t.Errorf("Response Data len != 1") @@ -978,7 +978,7 @@ func TestClient(t *testing.T) { data, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, NewOperations().Add(1, 1)) if err != nil { - t.Fatalf("Failed to Upsert (insert): %s", err.Error()) + t.Fatalf("Failed to Upsert (insert): %s", err) } if data == nil { t.Fatalf("Response is nil after Upsert (insert)") @@ -986,7 +986,7 @@ func TestClient(t *testing.T) { data, err = conn.Upsert(spaceNo, []interface{}{uint(3), 1}, NewOperations().Add(1, 1)) if err != nil { - t.Fatalf("Failed to Upsert (update): %s", err.Error()) + t.Fatalf("Failed to Upsert (update): %s", err) } if data == nil { t.Errorf("Response is nil after Upsert (update)") @@ -996,7 +996,7 @@ func TestClient(t *testing.T) { for i := 10; i < 20; i++ { data, err = conn.Replace(spaceNo, []interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) if err != nil { - t.Fatalf("Failed to Replace: %s", err.Error()) + t.Fatalf("Failed to Replace: %s", err) } if data == nil { t.Errorf("Response is nil after Replace") @@ -1004,7 +1004,7 @@ func TestClient(t *testing.T) { } data, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}) if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 1 { t.Fatalf("Response Data len != 1") @@ -1023,7 +1023,7 @@ func TestClient(t *testing.T) { // Select empty data, err = conn.Select(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(30)}) if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 0 { t.Errorf("Response Data len != 0") @@ -1033,7 +1033,7 @@ func TestClient(t *testing.T) { var tpl []Tuple err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}, &tpl) if err != nil { - t.Fatalf("Failed to SelectTyped: %s", err.Error()) + t.Fatalf("Failed to SelectTyped: %s", err) } if len(tpl) != 1 { t.Errorf("Result len of SelectTyped != 1") @@ -1045,7 +1045,7 @@ func TestClient(t *testing.T) { var singleTpl = Tuple{} err = conn.GetTyped(spaceNo, indexNo, []interface{}{uint(10)}, &singleTpl) if err != nil { - t.Fatalf("Failed to GetTyped: %s", err.Error()) + t.Fatalf("Failed to GetTyped: %s", err) } if singleTpl.Id != 10 { t.Errorf("Bad value loaded from GetTyped") @@ -1055,7 +1055,7 @@ func TestClient(t *testing.T) { var tpl1 [1]Tuple err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(10)}, &tpl1) if err != nil { - t.Fatalf("Failed to SelectTyped: %s", err.Error()) + t.Fatalf("Failed to SelectTyped: %s", err) } if len(tpl) != 1 { t.Errorf("Result len of SelectTyped != 1") @@ -1067,7 +1067,7 @@ func TestClient(t *testing.T) { var singleTpl2 Tuple err = conn.GetTyped(spaceNo, indexNo, []interface{}{uint(30)}, &singleTpl2) if err != nil { - t.Fatalf("Failed to GetTyped: %s", err.Error()) + t.Fatalf("Failed to GetTyped: %s", err) } if singleTpl2.Id != 0 { t.Errorf("Bad value loaded from GetTyped") @@ -1077,7 +1077,7 @@ func TestClient(t *testing.T) { var tpl2 []Tuple err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{uint(30)}, &tpl2) if err != nil { - t.Fatalf("Failed to SelectTyped: %s", err.Error()) + t.Fatalf("Failed to SelectTyped: %s", err) } if len(tpl2) != 0 { t.Errorf("Result len of SelectTyped != 1") @@ -1086,7 +1086,7 @@ func TestClient(t *testing.T) { // Call16 data, err = conn.Call16("box.info", []interface{}{"box.schema.SPACE_ID"}) if err != nil { - t.Fatalf("Failed to Call16: %s", err.Error()) + t.Fatalf("Failed to Call16: %s", err) } if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") @@ -1112,7 +1112,7 @@ func TestClient(t *testing.T) { // Eval data, err = conn.Eval("return 5 + 6", []interface{}{}) if err != nil { - t.Fatalf("Failed to Eval: %s", err.Error()) + t.Fatalf("Failed to Eval: %s", err) } if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") @@ -1157,7 +1157,7 @@ func TestClientSessionPush(t *testing.T) { // Future.Get ignores push messages. data, err := fut1.Get() if err != nil { - t.Errorf("Failed to Call17: %s", err.Error()) + t.Errorf("Failed to Call17: %s", err) } else if len(data) < 1 { t.Errorf("Response.Data is empty after Call17Async") } else if val, err := test_helpers.ConvertUint64(data[0]); err != nil || val != pushMax { @@ -1185,7 +1185,7 @@ func TestClientSessionPush(t *testing.T) { } data, err := resp.Decode() if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) + t.Errorf("Failed to Decode: %s", err) break } if len(data) < 1 { @@ -1411,20 +1411,20 @@ func TestSQL(t *testing.T) { assert.NoError(t, err, "Failed to Execute, query: %s", test.Query) assert.NotNil(t, resp, "Response is nil after Execute\nQuery number: %d", i) data, err := resp.Decode() - assert.Nil(t, err, "Failed to Decode") + assert.NoError(t, err, "Failed to Decode") for j := range data { assert.Equal(t, data[j], test.data[j], "Response data is wrong") } exResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err := exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + assert.NoError(t, err, "Error while getting SQLInfo") assert.Equal(t, sqlInfo.AffectedCount, test.sqlInfo.AffectedCount, "Affected count is wrong") errorMsg := "Response Metadata is wrong" metaData, err := exResp.MetaData() - assert.Nil(t, err, "Error while getting MetaData") + assert.NoError(t, err, "Error while getting MetaData") for j := range metaData { assert.Equal(t, metaData[j], test.metaData[j], errorMsg) } @@ -1510,14 +1510,14 @@ func TestSQLBindings(t *testing.T) { req := NewExecuteRequest(selectNamedQuery2).Args(bind) resp, err := conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } data, err := resp.Decode() if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) + t.Errorf("Failed to Decode: %s", err) } if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with named arguments failed") @@ -1525,7 +1525,7 @@ func TestSQLBindings(t *testing.T) { exResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") metaData, err := exResp.MetaData() - assert.Nil(t, err, "Error while getting MetaData") + assert.NoError(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1537,14 +1537,14 @@ func TestSQLBindings(t *testing.T) { req := NewExecuteRequest(selectPosQuery2).Args(sqlBind5) resp, err := conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } data, err := resp.Decode() if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) + t.Errorf("Failed to Decode: %s", err) } if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") @@ -1552,7 +1552,7 @@ func TestSQLBindings(t *testing.T) { exResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") metaData, err := exResp.MetaData() - assert.Nil(t, err, "Error while getting MetaData") + assert.NoError(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1563,14 +1563,14 @@ func TestSQLBindings(t *testing.T) { req = NewExecuteRequest(mixedQuery).Args(sqlBind6) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } data, err = resp.Decode() if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) + t.Errorf("Failed to Decode: %s", err) } if reflect.DeepEqual(data[0], []interface{}{1, testData[1]}) { t.Error("Select with positioned arguments failed") @@ -1578,7 +1578,7 @@ func TestSQLBindings(t *testing.T) { exResp, ok = resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") metaData, err = exResp.MetaData() - assert.Nil(t, err, "Error while getting MetaData") + assert.NoError(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1596,7 +1596,7 @@ func TestStressSQL(t *testing.T) { req := NewExecuteRequest(createTableQuery) resp, err := conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to create an Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") @@ -1604,7 +1604,7 @@ func TestStressSQL(t *testing.T) { exResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err := exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + assert.NoError(t, err, "Error while getting SQLInfo") if sqlInfo.AffectedCount != 1 { t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } @@ -1613,7 +1613,7 @@ func TestStressSQL(t *testing.T) { req = NewExecuteRequest(createTableQuery) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to create an Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") @@ -1624,14 +1624,15 @@ func TestStressSQL(t *testing.T) { tntErr, ok := err.(Error) assert.True(t, ok) assert.Equal(t, iproto.ER_SPACE_EXISTS, tntErr.Code) - if iproto.Error(resp.Header().Code) != iproto.ER_SPACE_EXISTS { - t.Fatalf("Unexpected response code: %d", resp.Header().Code) + if resp.Header().Error != iproto.ER_SPACE_EXISTS { + t.Fatalf("Unexpected response error: %d", resp.Header().Error) } + prevErr := err exResp, ok = resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err = exResp.SQLInfo() - assert.Nil(t, err, "Unexpected error") + assert.Equal(t, prevErr, err) if sqlInfo.AffectedCount != 0 { t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } @@ -1640,12 +1641,12 @@ func TestStressSQL(t *testing.T) { req = NewExecuteRequest(createTableQuery).Args(nil) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to create an Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Header().Code == OkCode { + if resp.Header().Error == ErrorNo { t.Fatal("Unexpected successful Execute") } exResp, ok = resp.(*ExecuteResponse) @@ -1660,12 +1661,12 @@ func TestStressSQL(t *testing.T) { req = NewExecuteRequest("") resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to create an Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Header().Code == OkCode { + if resp.Header().Error == ErrorNo { t.Fatal("Unexpected successful Execute") } exResp, ok = resp.(*ExecuteResponse) @@ -1680,7 +1681,7 @@ func TestStressSQL(t *testing.T) { req = NewExecuteRequest(dropQuery2) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") @@ -1688,7 +1689,7 @@ func TestStressSQL(t *testing.T) { exResp, ok = resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err = exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + assert.NoError(t, err, "Error while getting SQLInfo") if sqlInfo.AffectedCount != 1 { t.Errorf("Incorrect count of dropped spaces: %d", sqlInfo.AffectedCount) } @@ -1697,12 +1698,12 @@ func TestStressSQL(t *testing.T) { req = NewExecuteRequest(dropQuery2) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to create an Execute: %s", err.Error()) + t.Fatalf("Failed to create an Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } - if resp.Header().Code == OkCode { + if resp.Header().Error == ErrorNo { t.Fatal("Unexpected successful Execute") } _, err = resp.Decode() @@ -1712,7 +1713,9 @@ func TestStressSQL(t *testing.T) { exResp, ok = resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err = exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + if err == nil { + t.Fatal("Unexpected lack of error") + } if sqlInfo.AffectedCount != 0 { t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } @@ -1738,7 +1741,7 @@ func TestNewPrepared(t *testing.T) { } data, err := resp.Decode() if err != nil { - t.Errorf("Failed to Decode: %s", err.Error()) + t.Errorf("Failed to Decode: %s", err) } if reflect.DeepEqual(data[0], []interface{}{1, "test"}) { t.Error("Select with named arguments failed") @@ -1746,7 +1749,7 @@ func TestNewPrepared(t *testing.T) { prepResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") metaData, err := prepResp.MetaData() - assert.Nil(t, err, "Error while getting MetaData") + assert.NoError(t, err, "Error while getting MetaData") if metaData[0].FieldType != "unsigned" || metaData[0].FieldName != "NAME0" || metaData[1].FieldType != "string" || @@ -1797,7 +1800,7 @@ func TestConnection_DoWithStrangerConn(t *testing.T) { " connection or connection pool") conn1 := &Connection{} - req := test_helpers.NewStrangerRequest() + req := test_helpers.NewMockRequest() _, err := conn1.Do(req).Get() if err == nil { @@ -1841,7 +1844,7 @@ func TestConnection_SetSchema_Changes(t *testing.T) { req.Tuple([]interface{}{uint(1010), "Tarantool"}) _, err := conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } s, err := GetSchema(conn) @@ -1858,7 +1861,7 @@ func TestConnection_SetSchema_Changes(t *testing.T) { reqS.Key([]interface{}{uint(1010)}) data, err := conn.Do(reqS).Get() if err != nil { - t.Fatalf("failed to Select: %s", err.Error()) + t.Fatalf("failed to Select: %s", err) } if data[0].([]interface{})[1] != "Tarantool" { t.Errorf("wrong Select body: %v", data) @@ -2051,6 +2054,35 @@ func TestSchema_IsNullable(t *testing.T) { } } +func TestNewPreparedFromResponse(t *testing.T) { + var ( + ErrNilResponsePassed = fmt.Errorf("passed nil response") + ErrNilResponseData = fmt.Errorf("response Data is nil") + ErrWrongDataFormat = fmt.Errorf("response Data format is wrong") + ) + + testConn := &Connection{} + testCases := []struct { + name string + resp Response + expectedError error + }{ + {"ErrNilResponsePassed", nil, ErrNilResponsePassed}, + {"ErrNilResponseData", test_helpers.NewMockResponse(t, nil), + ErrNilResponseData}, + {"ErrWrongDataFormat", test_helpers.NewMockResponse(t, []interface{}{}), + ErrWrongDataFormat}, + {"ErrWrongDataFormat", test_helpers.NewMockResponse(t, []interface{}{"test"}), + ErrWrongDataFormat}, + } + for _, testCase := range testCases { + t.Run("Expecting error "+testCase.name, func(t *testing.T) { + _, err := NewPreparedFromResponse(testConn, testCase.resp) + assert.Equal(t, testCase.expectedError, err) + }) + } +} + func TestClientNamed(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() @@ -2058,7 +2090,7 @@ func TestClientNamed(t *testing.T) { // Insert data, err := conn.Insert(spaceName, []interface{}{uint(1001), "hello2", "world2"}) if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } if data == nil { t.Errorf("Response is nil after Insert") @@ -2067,7 +2099,7 @@ func TestClientNamed(t *testing.T) { // Delete data, err = conn.Delete(spaceName, indexName, []interface{}{uint(1001)}) if err != nil { - t.Fatalf("Failed to Delete: %s", err.Error()) + t.Fatalf("Failed to Delete: %s", err) } if data == nil { t.Errorf("Response is nil after Delete") @@ -2076,7 +2108,7 @@ func TestClientNamed(t *testing.T) { // Replace data, err = conn.Replace(spaceName, []interface{}{uint(1002), "hello", "world"}) if err != nil { - t.Fatalf("Failed to Replace: %s", err.Error()) + t.Fatalf("Failed to Replace: %s", err) } if data == nil { t.Errorf("Response is nil after Replace") @@ -2088,7 +2120,7 @@ func TestClientNamed(t *testing.T) { uint(1002)}, NewOperations().Assign(1, "buy").Delete(2, 1)) if err != nil { - t.Fatalf("Failed to Update: %s", err.Error()) + t.Fatalf("Failed to Update: %s", err) } if data == nil { t.Errorf("Response is nil after Update") @@ -2098,7 +2130,7 @@ func TestClientNamed(t *testing.T) { data, err = conn.Upsert(spaceName, []interface{}{uint(1003), 1}, NewOperations().Add(1, 1)) if err != nil { - t.Fatalf("Failed to Upsert (insert): %s", err.Error()) + t.Fatalf("Failed to Upsert (insert): %s", err) } if data == nil { t.Errorf("Response is nil after Upsert (insert)") @@ -2106,7 +2138,7 @@ func TestClientNamed(t *testing.T) { data, err = conn.Upsert(spaceName, []interface{}{uint(1003), 1}, NewOperations().Add(1, 1)) if err != nil { - t.Fatalf("Failed to Upsert (update): %s", err.Error()) + t.Fatalf("Failed to Upsert (update): %s", err) } if data == nil { t.Errorf("Response is nil after Upsert (update)") @@ -2117,7 +2149,7 @@ func TestClientNamed(t *testing.T) { data, err = conn.Replace(spaceName, []interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) if err != nil { - t.Fatalf("Failed to Replace: %s", err.Error()) + t.Fatalf("Failed to Replace: %s", err) } if data == nil { t.Errorf("Response is nil after Replace") @@ -2125,7 +2157,7 @@ func TestClientNamed(t *testing.T) { } data, err = conn.Select(spaceName, indexName, 0, 1, IterEq, []interface{}{uint(1010)}) if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if data == nil { t.Errorf("Response is nil after Select") @@ -2135,7 +2167,7 @@ func TestClientNamed(t *testing.T) { var tpl []Tuple err = conn.SelectTyped(spaceName, indexName, 0, 1, IterEq, []interface{}{uint(1010)}, &tpl) if err != nil { - t.Fatalf("Failed to SelectTyped: %s", err.Error()) + t.Fatalf("Failed to SelectTyped: %s", err) } if len(tpl) != 1 { t.Errorf("Result len of SelectTyped != 1") @@ -2155,7 +2187,7 @@ func TestClientRequestObjects(t *testing.T) { req = NewPingRequest() data, err := conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Ping: %s", err.Error()) + t.Fatalf("Failed to Ping: %s", err) } if len(data) != 0 { t.Errorf("Response Body len != 0") @@ -2172,7 +2204,7 @@ func TestClientRequestObjects(t *testing.T) { Tuple([]interface{}{uint(i), fmt.Sprintf("val %d", i), "bla"}) data, err = conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } if len(data) != 1 { t.Fatalf("Response Body len != 1") @@ -2201,7 +2233,7 @@ func TestClientRequestObjects(t *testing.T) { Tuple([]interface{}{uint(i), fmt.Sprintf("val %d", i), "blar"}) data, err = conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) + t.Fatalf("Failed to Decode: %s", err) } if len(data) != 1 { t.Fatalf("Response Body len != 1") @@ -2229,7 +2261,7 @@ func TestClientRequestObjects(t *testing.T) { Key([]interface{}{uint(1016)}) data, err = conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Delete: %s", err.Error()) + t.Fatalf("Failed to Delete: %s", err) } if data == nil { t.Fatalf("Response data is nil after Delete") @@ -2260,7 +2292,7 @@ func TestClientRequestObjects(t *testing.T) { Key([]interface{}{uint(1010)}) data, err = conn.Do(req).Get() if err != nil { - t.Errorf("Failed to Update: %s", err.Error()) + t.Errorf("Failed to Update: %s", err) } if data == nil { t.Fatalf("Response data is nil after Update") @@ -2289,7 +2321,7 @@ func TestClientRequestObjects(t *testing.T) { Operations(NewOperations().Assign(1, "bye").Insert(2, 1)) data, err = conn.Do(req).Get() if err != nil { - t.Errorf("Failed to Update: %s", err.Error()) + t.Errorf("Failed to Update: %s", err) } if len(data) != 1 { t.Fatalf("Response Data len != 1") @@ -2313,7 +2345,7 @@ func TestClientRequestObjects(t *testing.T) { Tuple([]interface{}{uint(1010), "hi", "hi"}) data, err = conn.Do(req).Get() if err != nil { - t.Errorf("Failed to Upsert (update): %s", err.Error()) + t.Errorf("Failed to Upsert (update): %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -2325,7 +2357,7 @@ func TestClientRequestObjects(t *testing.T) { Operations(NewOperations().Assign(2, "bye")) data, err = conn.Do(req).Get() if err != nil { - t.Errorf("Failed to Upsert (update): %s", err.Error()) + t.Errorf("Failed to Upsert (update): %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -2355,7 +2387,7 @@ func TestClientRequestObjects(t *testing.T) { req = NewEvalRequest("return 5 + 6") data, err = conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Eval: %s", err.Error()) + t.Fatalf("Failed to Eval: %s", err) } if len(data) < 1 { t.Errorf("Response.Data is empty after Eval") @@ -2376,14 +2408,14 @@ func TestClientRequestObjects(t *testing.T) { req = NewExecuteRequest(createTableQuery) resp, err := conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } data, err = resp.Decode() if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) + t.Fatalf("Failed to Decode: %s", err) } if len(data) != 0 { t.Fatalf("Response Body len != 0") @@ -2391,7 +2423,7 @@ func TestClientRequestObjects(t *testing.T) { exResp, ok := resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err := exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + assert.NoError(t, err, "Error while getting SQLInfo") if sqlInfo.AffectedCount != 1 { t.Errorf("Incorrect count of created spaces: %d", sqlInfo.AffectedCount) } @@ -2399,14 +2431,14 @@ func TestClientRequestObjects(t *testing.T) { req = NewExecuteRequest(dropQuery2) resp, err = conn.Do(req).GetResponse() if err != nil { - t.Fatalf("Failed to Execute: %s", err.Error()) + t.Fatalf("Failed to Execute: %s", err) } if resp == nil { t.Fatal("Response is nil after Execute") } data, err = resp.Decode() if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) + t.Fatalf("Failed to Decode: %s", err) } if len(data) != 0 { t.Fatalf("Response Body len != 0") @@ -2414,7 +2446,7 @@ func TestClientRequestObjects(t *testing.T) { exResp, ok = resp.(*ExecuteResponse) assert.True(t, ok, "Got wrong response type") sqlInfo, err = exResp.SQLInfo() - assert.Nil(t, err, "Error while getting SQLInfo") + assert.NoError(t, err, "Error while getting SQLInfo") if sqlInfo.AffectedCount != 1 { t.Errorf("Incorrect count of dropped spaces: %d", sqlInfo.AffectedCount) } @@ -2437,14 +2469,14 @@ func testConnectionDoSelectRequestCheck(t *testing.T, t.Helper() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if resp == nil { t.Fatalf("Response is nil after Select") } respPos, err := resp.Pos() if err != nil { - t.Errorf("Error while getting Pos: %s", err.Error()) + t.Errorf("Error while getting Pos: %s", err) } if !pos && respPos != nil { t.Errorf("Response should not have a position descriptor") @@ -2454,7 +2486,7 @@ func testConnectionDoSelectRequestCheck(t *testing.T, } data, err := resp.Decode() if err != nil { - t.Fatalf("Failed to Decode: %s", err.Error()) + t.Fatalf("Failed to Decode: %s", err) } if len(data) != dataLen { t.Fatalf("Response Data len %d != %d", len(data), dataLen) @@ -2608,7 +2640,7 @@ func TestConnectionDoSelectRequest_pagination_pos(t *testing.T) { testConnectionDoSelectRequestCheck(t, selResp, err, true, 2, 1010) selPos, err := selResp.Pos() - assert.Nil(t, err, "Error while getting Pos") + assert.NoError(t, err, "Error while getting Pos") resp, err = conn.Do(req.After(selPos)).GetResponse() selResp, ok = resp.(*SelectResponse) @@ -2652,7 +2684,7 @@ func TestClientRequestObjectsWithNilContext(t *testing.T) { req := NewPingRequest().Context(nil) //nolint data, err := conn.Do(req).Get() if err != nil { - t.Fatalf("Failed to Ping: %s", err.Error()) + t.Fatalf("Failed to Ping: %s", err) } if len(data) != 0 { t.Errorf("Response Body len != 0") @@ -2701,9 +2733,8 @@ func (req *waitCtxRequest) Async() bool { } func (req *waitCtxRequest) Response(header Header, body io.Reader) (Response, error) { - resp := BaseResponse{} - resp.SetHeader(header) - return &resp, nil + resp, err := test_helpers.CreateMockResponse(header, body) + return resp, err } func TestClientRequestObjectsWithContext(t *testing.T) { @@ -2753,13 +2784,13 @@ func TestComplexStructs(t *testing.T) { tuple := Tuple2{Cid: 777, Orig: "orig", Members: []Member{{"lol", "", 1}, {"wut", "", 3}}} _, err = conn.Replace(spaceNo, &tuple) if err != nil { - t.Fatalf("Failed to insert: %s", err.Error()) + t.Fatalf("Failed to insert: %s", err) } var tuples [1]Tuple2 err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{777}, &tuples) if err != nil { - t.Fatalf("Failed to selectTyped: %s", err.Error()) + t.Fatalf("Failed to selectTyped: %s", err) } if len(tuples) != 1 { @@ -2801,7 +2832,7 @@ func TestStream_IdValues(t *testing.T) { stream.Id = id _, err := stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Ping: %s", err.Error()) + t.Fatalf("Failed to Ping: %s", err) } }) } @@ -2823,7 +2854,7 @@ func TestStream_Commit(t *testing.T) { req = NewBeginRequest() _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Begin: %s", err.Error()) + t.Fatalf("Failed to Begin: %s", err) } // Insert in stream @@ -2831,7 +2862,7 @@ func TestStream_Commit(t *testing.T) { Tuple([]interface{}{uint(1001), "hello2", "world2"}) _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } defer test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{uint(1001)}) @@ -2845,7 +2876,7 @@ func TestStream_Commit(t *testing.T) { Key([]interface{}{uint(1001)}) data, err := conn.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -2854,7 +2885,7 @@ func TestStream_Commit(t *testing.T) { // Select in stream data, err = stream.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 1 { t.Fatalf("Response Data len != 1") @@ -2877,13 +2908,13 @@ func TestStream_Commit(t *testing.T) { req = NewCommitRequest() _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Commit: %s", err.Error()) + t.Fatalf("Failed to Commit: %s", err) } // Select outside of transaction data, err = conn.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 1 { t.Fatalf("Response Data len != 1") @@ -2919,7 +2950,7 @@ func TestStream_Rollback(t *testing.T) { req = NewBeginRequest() _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Begin: %s", err.Error()) + t.Fatalf("Failed to Begin: %s", err) } // Insert in stream @@ -2927,7 +2958,7 @@ func TestStream_Rollback(t *testing.T) { Tuple([]interface{}{uint(1001), "hello2", "world2"}) _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Insert: %s", err.Error()) + t.Fatalf("Failed to Insert: %s", err) } defer test_helpers.DeleteRecordByKey(t, conn, spaceNo, indexNo, []interface{}{uint(1001)}) @@ -2941,7 +2972,7 @@ func TestStream_Rollback(t *testing.T) { Key([]interface{}{uint(1001)}) data, err := conn.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -2950,7 +2981,7 @@ func TestStream_Rollback(t *testing.T) { // Select in stream data, err = stream.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 1 { t.Fatalf("Response Data len != 1") @@ -2973,13 +3004,13 @@ func TestStream_Rollback(t *testing.T) { req = NewRollbackRequest() _, err = stream.Do(req).Get() if err != nil { - t.Fatalf("Failed to Rollback: %s", err.Error()) + t.Fatalf("Failed to Rollback: %s", err) } // Select outside of transaction data, err = conn.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -2988,7 +3019,7 @@ func TestStream_Rollback(t *testing.T) { // Select inside of stream after rollback _, err = stream.Do(selectReq).Get() if err != nil { - t.Fatalf("Failed to Select: %s", err.Error()) + t.Fatalf("Failed to Select: %s", err) } if len(data) != 0 { t.Fatalf("Response Data len != 0") @@ -3084,7 +3115,7 @@ func TestStream_DoWithStrangerConn(t *testing.T) { conn := &Connection{} stream, _ := conn.NewStream() - req := test_helpers.NewStrangerRequest() + req := test_helpers.NewMockRequest() _, err := stream.Do(req).Get() if err == nil { diff --git a/test_helpers/doer.go b/test_helpers/doer.go new file mode 100644 index 000000000..c33ff0e69 --- /dev/null +++ b/test_helpers/doer.go @@ -0,0 +1,69 @@ +package test_helpers + +import ( + "bytes" + "testing" + + "github.com/tarantool/go-tarantool/v2" +) + +type doerResponse struct { + resp *MockResponse + err error +} + +// MockDoer is an implementation of the Doer interface +// used for testing purposes. +type MockDoer struct { + // Requests is a slice of received requests. + // It could be used to compare incoming requests with expected. + Requests []tarantool.Request + responses []doerResponse + t *testing.T +} + +// NewMockDoer creates a MockDoer by given responses. +// Each response could be one of two types: MockResponse or error. +func NewMockDoer(t *testing.T, responses ...interface{}) MockDoer { + t.Helper() + + mockDoer := MockDoer{t: t} + for _, response := range responses { + doerResp := doerResponse{} + + switch resp := response.(type) { + case *MockResponse: + doerResp.resp = resp + case error: + doerResp.err = resp + default: + t.Fatalf("unsupported type: %T", response) + } + + mockDoer.responses = append(mockDoer.responses, doerResp) + } + return mockDoer +} + +// Do returns a future with the current response or an error. +// It saves the current request into MockDoer.Requests. +func (doer *MockDoer) Do(req tarantool.Request) *tarantool.Future { + doer.Requests = append(doer.Requests, req) + + mockReq := NewMockRequest() + fut := tarantool.NewFuture(mockReq) + + if len(doer.responses) == 0 { + doer.t.Fatalf("list of responses is empty") + } + response := doer.responses[0] + + if response.err != nil { + fut.SetError(response.err) + } else { + fut.SetResponse(response.resp.header, bytes.NewBuffer(response.resp.data)) + } + doer.responses = doer.responses[1:] + + return fut +} diff --git a/test_helpers/example_test.go b/test_helpers/example_test.go new file mode 100644 index 000000000..6272d737d --- /dev/null +++ b/test_helpers/example_test.go @@ -0,0 +1,36 @@ +package test_helpers_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tarantool/go-tarantool/v2" + "github.com/tarantool/go-tarantool/v2/test_helpers" +) + +func TestExampleMockDoer(t *testing.T) { + mockDoer := test_helpers.NewMockDoer(t, + test_helpers.NewMockResponse(t, []interface{}{"some data"}), + fmt.Errorf("some error"), + test_helpers.NewMockResponse(t, "some typed data"), + fmt.Errorf("some error"), + ) + + data, err := mockDoer.Do(tarantool.NewPingRequest()).Get() + assert.NoError(t, err) + assert.Equal(t, []interface{}{"some data"}, data) + + data, err = mockDoer.Do(tarantool.NewSelectRequest("foo")).Get() + assert.EqualError(t, err, "some error") + assert.Nil(t, data) + + var stringData string + err = mockDoer.Do(tarantool.NewInsertRequest("space")).GetTyped(&stringData) + assert.NoError(t, err) + assert.Equal(t, "some typed data", stringData) + + err = mockDoer.Do(tarantool.NewPrepareRequest("expr")).GetTyped(&stringData) + assert.EqualError(t, err, "some error") + assert.Nil(t, data) +} diff --git a/test_helpers/request.go b/test_helpers/request.go new file mode 100644 index 000000000..3756a2b54 --- /dev/null +++ b/test_helpers/request.go @@ -0,0 +1,52 @@ +package test_helpers + +import ( + "context" + "io" + + "github.com/tarantool/go-iproto" + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-tarantool/v2" +) + +// MockRequest is an empty mock request used for testing purposes. +type MockRequest struct { +} + +// NewMockRequest creates an empty MockRequest. +func NewMockRequest() *MockRequest { + return &MockRequest{} +} + +// Type returns an iproto type for MockRequest. +func (req *MockRequest) Type() iproto.Type { + return iproto.Type(0) +} + +// Async returns if MockRequest expects a response. +func (req *MockRequest) Async() bool { + return false +} + +// Body fills an msgpack.Encoder with the watch request body. +func (req *MockRequest) Body(resolver tarantool.SchemaResolver, enc *msgpack.Encoder) error { + return nil +} + +// Conn returns the Connection object the request belongs to. +func (req *MockRequest) Conn() *tarantool.Connection { + return &tarantool.Connection{} +} + +// Ctx returns a context of the MockRequest. +func (req *MockRequest) Ctx() context.Context { + return nil +} + +// Response creates a response for the MockRequest. +func (req *MockRequest) Response(header tarantool.Header, + body io.Reader) (tarantool.Response, error) { + resp, err := CreateMockResponse(header, body) + return resp, err +} diff --git a/test_helpers/request_mock.go b/test_helpers/request_mock.go deleted file mode 100644 index 980c35db2..000000000 --- a/test_helpers/request_mock.go +++ /dev/null @@ -1,45 +0,0 @@ -package test_helpers - -import ( - "context" - "io" - - "github.com/tarantool/go-iproto" - "github.com/vmihailenco/msgpack/v5" - - "github.com/tarantool/go-tarantool/v2" -) - -type StrangerRequest struct { -} - -func NewStrangerRequest() *StrangerRequest { - return &StrangerRequest{} -} - -func (sr *StrangerRequest) Type() iproto.Type { - return iproto.Type(0) -} - -func (sr *StrangerRequest) Async() bool { - return false -} - -func (sr *StrangerRequest) Body(resolver tarantool.SchemaResolver, enc *msgpack.Encoder) error { - return nil -} - -func (sr *StrangerRequest) Conn() *tarantool.Connection { - return &tarantool.Connection{} -} - -func (sr *StrangerRequest) Ctx() context.Context { - return nil -} - -func (sr *StrangerRequest) Response(header tarantool.Header, - body io.Reader) (tarantool.Response, error) { - resp := tarantool.BaseResponse{} - resp.SetHeader(header) - return &resp, nil -} diff --git a/test_helpers/response.go b/test_helpers/response.go new file mode 100644 index 000000000..4a28400c0 --- /dev/null +++ b/test_helpers/response.go @@ -0,0 +1,74 @@ +package test_helpers + +import ( + "bytes" + "io" + "io/ioutil" + "testing" + + "github.com/vmihailenco/msgpack/v5" + + "github.com/tarantool/go-tarantool/v2" +) + +// MockResponse is a mock response used for testing purposes. +type MockResponse struct { + // header contains response header + header tarantool.Header + // data contains data inside a response. + data []byte +} + +// NewMockResponse creates a new MockResponse with an empty header and the given data. +// body should be passed as a structure to be encoded. +// The encoded body is served as response data and will be decoded once the +// response is decoded. +func NewMockResponse(t *testing.T, body interface{}) *MockResponse { + t.Helper() + + buf := bytes.NewBuffer([]byte{}) + enc := msgpack.NewEncoder(buf) + + err := enc.Encode(body) + if err != nil { + t.Errorf("unexpected error while encoding: %s", err) + } + + return &MockResponse{data: buf.Bytes()} +} + +// CreateMockResponse creates a MockResponse from the header and a data, +// packed inside an io.Reader. +func CreateMockResponse(header tarantool.Header, body io.Reader) (*MockResponse, error) { + if body == nil { + return &MockResponse{header: header, data: nil}, nil + } + data, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + return &MockResponse{header: header, data: data}, nil +} + +// Header returns a header for the MockResponse. +func (resp *MockResponse) Header() tarantool.Header { + return resp.header +} + +// Decode returns the result of decoding the response data as slice. +func (resp *MockResponse) Decode() ([]interface{}, error) { + if resp.data == nil { + return nil, nil + } + dec := msgpack.NewDecoder(bytes.NewBuffer(resp.data)) + return dec.DecodeSlice() +} + +// DecodeTyped returns the result of decoding the response data. +func (resp *MockResponse) DecodeTyped(res interface{}) error { + if resp.data == nil { + return nil + } + dec := msgpack.NewDecoder(bytes.NewBuffer(resp.data)) + return dec.Decode(res) +} From 9a1b8e425490b6b7e3216a601c9ccd55b3735288 Mon Sep 17 00:00:00 2001 From: DerekBum Date: Thu, 25 Jan 2024 11:43:09 +0300 Subject: [PATCH 4/4] ci: bump 1.10 master for macOS 1.10.14 build is flacky on macOS-12 runner. Bump to 1.10.15 hopefully helps with this issue. --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 34219a3b0..cba02c6ab 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -241,7 +241,7 @@ jobs: - macos-12 tarantool: - brew - - 1.10.14 + - 1.10.15 env: # Make sense only for non-brew jobs.