diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 311ffac95..5c3c84eae 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -60,6 +60,9 @@ jobs: - name: Run tests run: make test + - name: Run tests with call_17 + run: make test TAGS="go_tarantool_call_17" + - name: Run tests, collect code coverage data and send to Coveralls if: ${{ matrix.coveralls }} env: @@ -134,6 +137,13 @@ jobs: env: TEST_TNT_SSL: ${{matrix.ssl}} + - name: Run tests with call_17 + run: | + source tarantool-enterprise/env.sh + make test TAGS="go_tarantool_call_17" + env: + TEST_TNT_SSL: ${{matrix.ssl}} + - name: Run tests, collect code coverage data and send to Coveralls if: ${{ matrix.coveralls }} env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f4e2f887..ddb5cbc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. ### Changed +- Add `Call16` method, support build tag `go_tarantool_call_17` + to choose behavior for `Call` method (#125) + ### Fixed ## [1.6.0] - 2022-06-01 diff --git a/Makefile b/Makefile index d88ce323e..06aa01189 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ BENCH_REFERENCE_REPO := ${BENCH_PATH}/go-tarantool BENCH_OPTIONS := -bench=. -run=^Benchmark -benchmem -benchtime=${DURATION} -count=${COUNT} GO_TARANTOOL_URL := https://github.com/tarantool/go-tarantool GO_TARANTOOL_DIR := ${PROJECT_DIR}/${BENCH_PATH}/go-tarantool +TAGS := .PHONY: clean clean: @@ -33,7 +34,7 @@ golangci-lint: .PHONY: test test: - go test ./... -v -p 1 + go test -tags "$(TAGS)" ./... -v -p 1 .PHONY: testdata testdata: @@ -43,38 +44,38 @@ testdata: test-connection-pool: @echo "Running tests in connection_pool package" go clean -testcache - go test ./connection_pool/ -v -p 1 + go test -tags "$(TAGS)" ./connection_pool/ -v -p 1 .PHONY: test-multi test-multi: @echo "Running tests in multiconnection package" go clean -testcache - go test ./multi/ -v -p 1 + go test -tags "$(TAGS)" ./multi/ -v -p 1 .PHONY: test-queue test-queue: @echo "Running tests in queue package" cd ./queue/ && tarantool -e "require('queue')" go clean -testcache - go test ./queue/ -v -p 1 + go test -tags "$(TAGS)" ./queue/ -v -p 1 .PHONY: test-uuid test-uuid: @echo "Running tests in UUID package" go clean -testcache - go test ./uuid/ -v -p 1 + go test -tags "$(TAGS)" ./uuid/ -v -p 1 .PHONY: test-main test-main: @echo "Running tests in main package" go clean -testcache - go test . -v -p 1 + go test -tags "$(TAGS)" . -v -p 1 .PHONY: coverage coverage: go clean -testcache go get golang.org/x/tools/cmd/cover - go test ./... -v -p 1 -covermode=atomic -coverprofile=$(COVERAGE_FILE) -coverpkg=./... + go test -tags "$(TAGS)" ./... -v -p 1 -covermode=atomic -coverprofile=$(COVERAGE_FILE) -coverpkg=./... go tool cover -func=$(COVERAGE_FILE) .PHONY: coveralls @@ -94,7 +95,7 @@ ${BENCH_PATH} bench-deps: .PHONY: bench ${BENCH_FILE} bench: ${BENCH_PATH} @echo "Running benchmark tests from the current branch" - go test ${TEST_PATH} ${BENCH_OPTIONS} 2>&1 \ + go test -tags "$(TAGS)" ${TEST_PATH} ${BENCH_OPTIONS} 2>&1 \ | tee ${BENCH_FILE} benchstat ${BENCH_FILE} @@ -104,7 +105,7 @@ ${GO_TARANTOOL_DIR}: ${REFERENCE_FILE}: ${GO_TARANTOOL_DIR} @echo "Running benchmark tests from master for using results in bench-diff target" - cd ${GO_TARANTOOL_DIR} && git pull && go test ./... ${BENCH_OPTIONS} 2>&1 \ + cd ${GO_TARANTOOL_DIR} && git pull && go test ./... -tags "$(TAGS)" ${BENCH_OPTIONS} 2>&1 \ | tee ${REFERENCE_FILE} bench-diff: ${BENCH_FILES} diff --git a/README.md b/README.md index a2b77eaa7..7ddf956bf 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,20 @@ of any Go program. ### Build tags -To disable SSL support and linking with OpenSSL, you can use the tag: -``` -go_tarantool_ssl_disable -``` +We define multiple [build tags](https://pkg.go.dev/go/build#hdr-Build_Constraints). + +This allows us to introduce new features without losing backward compatibility. + +1. To disable SSL support and linking with OpenSSL, you can use the tag: + ``` + go_tarantool_ssl_disable + ``` +2. to change the default `Call` behavior from `Call16` to `Call17`, you can use the build + tag: + ``` + go_tarantool_call_17 + ``` + **Note:** In future releases, `Call17` may be used as default `Call` behavior. ## Documentation diff --git a/call_16_test.go b/call_16_test.go new file mode 100644 index 000000000..1aff4e62e --- /dev/null +++ b/call_16_test.go @@ -0,0 +1,27 @@ +//go:build !go_tarantool_call_17 +// +build !go_tarantool_call_17 + +package tarantool_test + +import ( + "testing" + + . "github.com/tarantool/go-tarantool" +) + +func TestConnection_Call(t *testing.T) { + var resp *Response + var err error + + conn := connect(t, server, opts) + defer conn.Close() + + // Call16 + resp, err = conn.Call("simple_incr", []interface{}{1}) + if err != nil { + t.Errorf("Failed to use Call") + } + if resp.Data[0].([]interface{})[0].(uint64) != 2 { + t.Errorf("result is not {{1}} : %v", resp.Data) + } +} diff --git a/call_17_test.go b/call_17_test.go new file mode 100644 index 000000000..83f441401 --- /dev/null +++ b/call_17_test.go @@ -0,0 +1,27 @@ +//go:build go_tarantool_call_17 +// +build go_tarantool_call_17 + +package tarantool_test + +import ( + "testing" + + . "github.com/tarantool/go-tarantool" +) + +func TestConnection_Call(t *testing.T) { + var resp *Response + var err error + + conn := connect(t, server, opts) + defer conn.Close() + + // Call17 + resp, err = conn.Call17("simple_incr", []interface{}{1}) + if err != nil { + t.Errorf("Failed to use Call") + } + if resp.Data[0].(uint64) != 2 { + t.Errorf("result is not {{1}} : %v", resp.Data) + } +} diff --git a/connection.go b/connection.go index 5f5425531..c5a55b36f 100644 --- a/connection.go +++ b/connection.go @@ -116,9 +116,9 @@ func (d defaultLogger) Report(event ConnLogKind, conn *Connection, v ...interfac // // ATTENTION: result argument for *Typed methods should deserialize from // msgpack array, cause Tarantool always returns result as an array. -// For all space related methods and Call* (but not Call17*) methods Tarantool +// For all space related methods and Call16* (but not Call17*) methods Tarantool // always returns array of array (array of tuples for space related methods). -// For Eval* and Call17* Tarantool always returns array, but does not forces +// For Eval* and Call* Tarantool always returns array, but does not forces // array of arrays. type Connection struct { addr string diff --git a/connection_pool/call_16_test.go b/connection_pool/call_16_test.go new file mode 100644 index 000000000..bf2ab2eb7 --- /dev/null +++ b/connection_pool/call_16_test.go @@ -0,0 +1,69 @@ +//go:build !go_tarantool_call_17 +// +build !go_tarantool_call_17 + +package connection_pool_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool/connection_pool" + "github.com/tarantool/go-tarantool/test_helpers" +) + +func TestCall(t *testing.T) { + roles := []bool{false, true, false, false, true} + + err := test_helpers.SetClusterRO(servers, connOpts, roles) + require.Nilf(t, err, "fail to set roles for cluster") + + connPool, err := connection_pool.Connect(servers, connOpts) + require.Nilf(t, err, "failed to connect") + require.NotNilf(t, connPool, "conn is nil after Connect") + + defer connPool.Close() + + // PreferRO + resp, err := connPool.Call("box.info", []interface{}{}, connection_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") + + val := resp.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`") + + // PreferRW + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") + + // RO + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") + + // RW + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") +} diff --git a/connection_pool/call_17_test.go b/connection_pool/call_17_test.go new file mode 100644 index 000000000..94e5bb888 --- /dev/null +++ b/connection_pool/call_17_test.go @@ -0,0 +1,69 @@ +//go:build go_tarantool_call_17 +// +build go_tarantool_call_17 + +package connection_pool_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool/connection_pool" + "github.com/tarantool/go-tarantool/test_helpers" +) + +func TestCall(t *testing.T) { + roles := []bool{false, true, false, false, true} + + err := test_helpers.SetClusterRO(servers, connOpts, roles) + require.Nilf(t, err, "fail to set roles for cluster") + + connPool, err := connection_pool.Connect(servers, connOpts) + require.Nilf(t, err, "failed to connect") + require.NotNilf(t, connPool, "conn is nil after Connect") + + defer connPool.Close() + + // PreferRO + resp, err := connPool.Call("box.info", []interface{}{}, connection_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") + + val := resp.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`") + + // PreferRW + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") + + // RO + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") + + // RW + resp, err = connPool.Call("box.info", []interface{}{}, connection_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") + + val = resp.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`") +} diff --git a/connection_pool/connection_pool.go b/connection_pool/connection_pool.go index d4a343911..be56806c5 100644 --- a/connection_pool/connection_pool.go +++ b/connection_pool/connection_pool.go @@ -255,8 +255,10 @@ func (connPool *ConnectionPool) Upsert(space interface{}, tuple, ops interface{} return conn.Upsert(space, tuple, ops) } -// Call calls registered Tarantool function. -// It uses request code for Tarantool 1.6, so result is converted to array of arrays. +// Call16 calls registered Tarantool function. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connPool *ConnectionPool) Call(functionName string, args interface{}, userMode Mode) (resp *tarantool.Response, err error) { conn, err := connPool.getNextConnection(userMode) if err != nil { @@ -266,8 +268,20 @@ func (connPool *ConnectionPool) Call(functionName string, args interface{}, user return conn.Call(functionName, args) } +// Call16 calls registered Tarantool function. +// It uses request code for Tarantool 1.6, so result is converted to array of arrays. +// Deprecated since Tarantool 1.7.2. +func (connPool *ConnectionPool) Call16(functionName string, args interface{}, userMode Mode) (resp *tarantool.Response, err error) { + conn, err := connPool.getNextConnection(userMode) + if err != nil { + return nil, err + } + + return conn.Call16(functionName, args) +} + // Call17 calls registered Tarantool function. -// It uses request code for Tarantool 1.7, so result is not converted +// It uses request code for Tarantool >= 1.7, so result is not converted // (though, keep in mind, result is always array). func (connPool *ConnectionPool) Call17(functionName string, args interface{}, userMode Mode) (resp *tarantool.Response, err error) { conn, err := connPool.getNextConnection(userMode) @@ -352,7 +366,9 @@ func (connPool *ConnectionPool) UpdateTyped(space, index interface{}, key, ops i } // CallTyped calls registered function. -// It uses request code for Tarantool 1.6, so result is converted to array of arrays. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connPool *ConnectionPool) CallTyped(functionName string, args interface{}, result interface{}, userMode Mode) (err error) { conn, err := connPool.getNextConnection(userMode) if err != nil { @@ -362,8 +378,20 @@ func (connPool *ConnectionPool) CallTyped(functionName string, args interface{}, return conn.CallTyped(functionName, args, result) } +// Call16Typed calls registered function. +// It uses request code for Tarantool 1.6, so result is converted to array of arrays. +// Deprecated since Tarantool 1.7.2. +func (connPool *ConnectionPool) Call16Typed(functionName string, args interface{}, result interface{}, userMode Mode) (err error) { + conn, err := connPool.getNextConnection(userMode) + if err != nil { + return err + } + + return conn.Call16Typed(functionName, args, result) +} + // Call17Typed calls registered function. -// It uses request code for Tarantool 1.7, so result is not converted +// It uses request code for Tarantool >= 1.7, so result is not converted // (though, keep in mind, result is always array). func (connPool *ConnectionPool) Call17Typed(functionName string, args interface{}, result interface{}, userMode Mode) (err error) { conn, err := connPool.getNextConnection(userMode) @@ -450,7 +478,9 @@ func (connPool *ConnectionPool) UpsertAsync(space interface{}, tuple interface{} } // CallAsync sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.6, so future's result is always array of arrays. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connPool *ConnectionPool) CallAsync(functionName string, args interface{}, userMode Mode) *tarantool.Future { conn, err := connPool.getNextConnection(userMode) if err != nil { @@ -460,8 +490,20 @@ func (connPool *ConnectionPool) CallAsync(functionName string, args interface{}, return conn.CallAsync(functionName, args) } +// Call16Async sends a call to registered Tarantool function and returns Future. +// It uses request code for Tarantool 1.6, so future's result is always array of arrays. +// Deprecated since Tarantool 1.7.2. +func (connPool *ConnectionPool) Call16Async(functionName string, args interface{}, userMode Mode) *tarantool.Future { + conn, err := connPool.getNextConnection(userMode) + if err != nil { + return tarantool.NewErrorFuture(err) + } + + return conn.Call16Async(functionName, args) +} + // Call17Async sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.7, so future's result will not be converted +// It uses request code for Tarantool >= 1.7, so future's result will not be converted // (though, keep in mind, result is always array). func (connPool *ConnectionPool) Call17Async(functionName string, args interface{}, userMode Mode) *tarantool.Future { conn, err := connPool.getNextConnection(userMode) diff --git a/connection_pool/connection_pool_test.go b/connection_pool/connection_pool_test.go index 4d98ddd9e..5b8a606cd 100644 --- a/connection_pool/connection_pool_test.go +++ b/connection_pool/connection_pool_test.go @@ -206,7 +206,7 @@ func TestClose(t *testing.T) { require.Nil(t, err) } -func TestCall(t *testing.T) { +func TestCall17(t *testing.T) { roles := []bool{false, true, false, false, true} err := test_helpers.SetClusterRO(servers, connOpts, roles) diff --git a/connection_pool/example_test.go b/connection_pool/example_test.go index 4da811daf..f567128dd 100644 --- a/connection_pool/example_test.go +++ b/connection_pool/example_test.go @@ -490,7 +490,7 @@ func ExampleConnectionPool_Update() { // Data [[key1 new_value]] } -func ExampleConnectionPool_Call17() { +func ExampleConnectionPool_Call() { pool, err := examplePool(testRoles) if err != nil { fmt.Println(err) diff --git a/connector.go b/connector.go index 0e79c6aaf..d8bdc13a0 100644 --- a/connector.go +++ b/connector.go @@ -15,6 +15,7 @@ type Connector interface { Update(space, index interface{}, key, ops interface{}) (resp *Response, err error) Upsert(space interface{}, tuple, ops interface{}) (resp *Response, err error) Call(functionName string, args interface{}) (resp *Response, err error) + Call16(functionName string, args interface{}) (resp *Response, err error) Call17(functionName string, args interface{}) (resp *Response, err error) Eval(expr string, args interface{}) (resp *Response, err error) Execute(expr string, args interface{}) (resp *Response, err error) @@ -26,6 +27,7 @@ type Connector interface { DeleteTyped(space, index interface{}, key interface{}, result interface{}) (err error) UpdateTyped(space, index interface{}, key, ops interface{}, result interface{}) (err error) CallTyped(functionName string, args interface{}, result interface{}) (err error) + Call16Typed(functionName string, args interface{}, result interface{}) (err error) Call17Typed(functionName string, args interface{}, result interface{}) (err error) EvalTyped(expr string, args interface{}, result interface{}) (err error) @@ -36,6 +38,7 @@ type Connector interface { UpdateAsync(space, index interface{}, key, ops interface{}) *Future UpsertAsync(space interface{}, tuple interface{}, ops interface{}) *Future CallAsync(functionName string, args interface{}) *Future + Call16Async(functionName string, args interface{}) *Future Call17Async(functionName string, args interface{}) *Future EvalAsync(expr string, args interface{}) *Future } diff --git a/const.go b/const.go index f542c8171..53e7017dd 100644 --- a/const.go +++ b/const.go @@ -6,11 +6,11 @@ const ( ReplaceRequest = 3 UpdateRequest = 4 DeleteRequest = 5 - CallRequest = 6 /* call in 1.6 format */ + Call16Request = 6 /* call in 1.6 format */ AuthRequest = 7 EvalRequest = 8 UpsertRequest = 9 - Call17Request = 10 + Call17Request = 10 /* call in >= 1.7 format */ ExecuteRequest = 11 PingRequest = 64 SubscribeRequest = 66 diff --git a/const_call_16.go b/const_call_16.go new file mode 100644 index 000000000..90ce7e6ce --- /dev/null +++ b/const_call_16.go @@ -0,0 +1,8 @@ +//go:build !go_tarantool_call_17 +// +build !go_tarantool_call_17 + +package tarantool + +const ( + CallRequest = Call16Request +) diff --git a/const_call_17.go b/const_call_17.go new file mode 100644 index 000000000..3b012c8ac --- /dev/null +++ b/const_call_17.go @@ -0,0 +1,8 @@ +//go:build go_tarantool_call_17 +// +build go_tarantool_call_17 + +package tarantool + +const ( + CallRequest = Call17Request +) diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index 65404c0d6..2910010cd 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -118,8 +118,8 @@ func Example_customUnpacking() { fmt.Println("Tuples (tuples2):", tuples2) // Call a function "func_name" returning a table of custom tuples. - var tuples3 []Tuple3 - err = conn.CallTyped("func_name", []interface{}{}, &tuples3) + var tuples3 [][]Tuple3 + err = conn.Call17Typed("func_name", []interface{}{}, &tuples3) if err != nil { log.Fatalf("Failed to CallTyped: %s", err.Error()) return @@ -131,6 +131,6 @@ func Example_customUnpacking() { // 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}]}] + // Tuples (tuples3): [[{{} 221 [{Moscow 34} {Minsk 23} {Kiev 31}]}]] } diff --git a/example_test.go b/example_test.go index b8f8ee110..4092bbd27 100644 --- a/example_test.go +++ b/example_test.go @@ -309,7 +309,7 @@ func ExampleConnection_Update() { // Data [[14 bye bla]] } -func ExampleConnection_Call17() { +func ExampleConnection_Call() { conn := example_connect() defer conn.Close() diff --git a/multi/call_16_test.go b/multi/call_16_test.go new file mode 100644 index 000000000..11a1a766d --- /dev/null +++ b/multi/call_16_test.go @@ -0,0 +1,33 @@ +//go:build !go_tarantool_call_17 +// +build !go_tarantool_call_17 + +package multi + +import ( + "testing" + + "github.com/tarantool/go-tarantool" +) + +func TestCall(t *testing.T) { + var resp *tarantool.Response + var err error + + multiConn, err := Connect([]string{server1, server2}, connOpts) + if err != nil { + t.Fatalf("Failed to connect: %s", err.Error()) + } + if multiConn == nil { + t.Fatalf("conn is nil after Connect") + } + defer multiConn.Close() + + // Call16 + resp, err = multiConn.Call("simple_incr", []interface{}{1}) + if err != nil { + t.Fatalf("Failed to use Call: %s", err.Error()) + } + if resp.Data[0].([]interface{})[0].(uint64) != 2 { + t.Fatalf("result is not {{1}} : %v", resp.Data) + } +} diff --git a/multi/call_17_test.go b/multi/call_17_test.go new file mode 100644 index 000000000..86a3cfc13 --- /dev/null +++ b/multi/call_17_test.go @@ -0,0 +1,33 @@ +//go:build go_tarantool_call_17 +// +build go_tarantool_call_17 + +package multi + +import ( + "testing" + + "github.com/tarantool/go-tarantool" +) + +func TestCall(t *testing.T) { + var resp *tarantool.Response + var err error + + multiConn, err := Connect([]string{server1, server2}, connOpts) + if err != nil { + t.Fatalf("Failed to connect: %s", err.Error()) + } + if multiConn == nil { + t.Fatalf("conn is nil after Connect") + } + defer multiConn.Close() + + // Call17 + resp, err = multiConn.Call("simple_incr", []interface{}{1}) + if err != nil { + t.Fatalf("Failed to use Call: %s", err.Error()) + } + if resp.Data[0].(uint64) != 2 { + t.Fatalf("result is not {{1}} : %v", resp.Data) + } +} diff --git a/multi/config.lua b/multi/config.lua index 25b0eb4f9..2b745185a 100644 --- a/multi/config.lua +++ b/multi/config.lua @@ -15,6 +15,12 @@ box.once("init", function() box.schema.user.grant('test', 'read,write,execute', 'universe') end) +local function simple_incr(a) + return a + 1 +end + +rawset(_G, 'simple_incr', simple_incr) + -- Set listen only when every other thing is configured. box.cfg{ listen = os.getenv("TEST_TNT_LISTEN"), diff --git a/multi/multi.go b/multi/multi.go index 89ec33ad6..edd6728b0 100644 --- a/multi/multi.go +++ b/multi/multi.go @@ -322,14 +322,23 @@ func (connMulti *ConnectionMulti) Upsert(space interface{}, tuple, ops interface } // Call calls registered Tarantool function. -// It uses request code for Tarantool 1.6, so result is converted to array of -// arrays. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connMulti *ConnectionMulti) Call(functionName string, args interface{}) (resp *tarantool.Response, err error) { return connMulti.getCurrentConnection().Call(functionName, args) } +// Call16 calls registered Tarantool function. +// It uses request code for Tarantool 1.6, so result is converted to array of +// arrays. +// Deprecated since Tarantool 1.7.2. +func (connMulti *ConnectionMulti) Call16(functionName string, args interface{}) (resp *tarantool.Response, err error) { + return connMulti.getCurrentConnection().Call16(functionName, args) +} + // Call17 calls registered Tarantool function. -// It uses request code for Tarantool 1.7, so result is not converted +// It uses request code for Tarantool >= 1.7, so result is not converted // (though, keep in mind, result is always array). func (connMulti *ConnectionMulti) Call17(functionName string, args interface{}) (resp *tarantool.Response, err error) { return connMulti.getCurrentConnection().Call17(functionName, args) @@ -383,14 +392,23 @@ func (connMulti *ConnectionMulti) UpdateTyped(space, index interface{}, key, ops } // CallTyped calls registered function. -// It uses request code for Tarantool 1.6, so result is converted to array of -// arrays. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connMulti *ConnectionMulti) CallTyped(functionName string, args interface{}, result interface{}) (err error) { return connMulti.getCurrentConnection().CallTyped(functionName, args, result) } +// Call16Typed calls registered function. +// It uses request code for Tarantool 1.6, so result is converted to array of +// arrays. +// Deprecated since Tarantool 1.7.2. +func (connMulti *ConnectionMulti) Call16Typed(functionName string, args interface{}, result interface{}) (err error) { + return connMulti.getCurrentConnection().Call16Typed(functionName, args, result) +} + // Call17Typed calls registered function. -// It uses request code for Tarantool 1.7, so result is not converted (though, +// It uses request code for Tarantool >= 1.7, so result is not converted (though, // keep in mind, result is always array) func (connMulti *ConnectionMulti) Call17Typed(functionName string, args interface{}, result interface{}) (err error) { return connMulti.getCurrentConnection().Call17Typed(functionName, args, result) @@ -437,14 +455,23 @@ func (connMulti *ConnectionMulti) UpsertAsync(space interface{}, tuple interface } // CallAsync sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.6, so future's result is always array -// of arrays. +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (connMulti *ConnectionMulti) CallAsync(functionName string, args interface{}) *tarantool.Future { return connMulti.getCurrentConnection().CallAsync(functionName, args) } +// Call16Async sends a call to registered Tarantool function and returns Future. +// It uses request code for Tarantool 1.6, so future's result is always array +// of arrays. +// Deprecated since Tarantool 1.7.2. +func (connMulti *ConnectionMulti) Call16Async(functionName string, args interface{}) *tarantool.Future { + return connMulti.getCurrentConnection().Call16Async(functionName, args) +} + // Call17Async sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.7, so future's result will not be converted +// It uses request code for Tarantool >= 1.7, so future's result will not be converted // (though, keep in mind, result is always array). func (connMulti *ConnectionMulti) Call17Async(functionName string, args interface{}) *tarantool.Future { return connMulti.getCurrentConnection().Call17Async(functionName, args) diff --git a/multi/multi_test.go b/multi/multi_test.go index e501ced13..0f84cdb4d 100644 --- a/multi/multi_test.go +++ b/multi/multi_test.go @@ -202,12 +202,35 @@ func TestRefresh(t *testing.T) { t.Errorf("Expect connection to exist") } - _, err := multiConn.Call(multiConn.opts.NodesGetFunctionName, []interface{}{}) + _, err := multiConn.Call17(multiConn.opts.NodesGetFunctionName, []interface{}{}) if err != nil { t.Error("Expect to get data after reconnect") } } +func TestCall17(t *testing.T) { + var resp *tarantool.Response + var err error + + multiConn, err := Connect([]string{server1, server2}, connOpts) + if err != nil { + t.Fatalf("Failed to connect: %s", err.Error()) + } + if multiConn == nil { + t.Fatalf("conn is nil after Connect") + } + defer multiConn.Close() + + // Call17 + resp, err = multiConn.Call17("simple_incr", []interface{}{1}) + if err != nil { + t.Fatalf("Failed to use Call: %s", err.Error()) + } + if resp.Data[0].(uint64) != 2 { + t.Fatalf("result is not {{1}} : %v", resp.Data) + } +} + // runTestMain is a body of TestMain function // (see https://pkg.go.dev/testing#hdr-Main). // Using defer + os.Exit is not works so TestMain body diff --git a/queue/queue.go b/queue/queue.go index ce0eeb49f..8a3e3e17d 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -189,7 +189,7 @@ func (q *queue) put(params ...interface{}) (*Task, error) { result: params[0], q: q, } - if err := q.conn.CallTyped(q.cmds.put, params, &qd); err != nil { + if err := q.conn.Call17Typed(q.cmds.put, params, &qd); err != nil { return nil, err } return qd.task, nil @@ -240,7 +240,7 @@ func (q *queue) take(params interface{}, result ...interface{}) (*Task, error) { if len(result) > 0 { qd.result = result[0] } - if err := q.conn.CallTyped(q.cmds.take, []interface{}{params}, &qd); err != nil { + if err := q.conn.Call17Typed(q.cmds.take, []interface{}{params}, &qd); err != nil { return nil, err } return qd.task, nil @@ -248,14 +248,14 @@ func (q *queue) take(params interface{}, result ...interface{}) (*Task, error) { // Drop queue. func (q *queue) Drop() error { - _, err := q.conn.Call(q.cmds.drop, []interface{}{}) + _, err := q.conn.Call17(q.cmds.drop, []interface{}{}) return err } // Look at a task without changing its state. func (q *queue) Peek(taskId uint64) (*Task, error) { qd := queueData{q: q} - if err := q.conn.CallTyped(q.cmds.peek, []interface{}{taskId}, &qd); err != nil { + if err := q.conn.Call17Typed(q.cmds.peek, []interface{}{taskId}, &qd); err != nil { return nil, err } return qd.task, nil @@ -278,7 +278,7 @@ func (q *queue) _release(taskId uint64, cfg Opts) (string, error) { } func (q *queue) produce(cmd string, params ...interface{}) (string, error) { qd := queueData{q: q} - if err := q.conn.CallTyped(cmd, params, &qd); err != nil || qd.task == nil { + if err := q.conn.Call17Typed(cmd, params, &qd); err != nil || qd.task == nil { return "", err } return qd.task.status, nil @@ -286,10 +286,10 @@ func (q *queue) produce(cmd string, params ...interface{}) (string, error) { // Reverse the effect of a bury request on one or more tasks. func (q *queue) Kick(count uint64) (uint64, error) { - resp, err := q.conn.Call(q.cmds.kick, []interface{}{count}) + resp, err := q.conn.Call17(q.cmds.kick, []interface{}{count}) var id uint64 if err == nil { - id = resp.Data[0].([]interface{})[0].(uint64) + id = resp.Data[0].(uint64) } return id, err } @@ -303,16 +303,13 @@ func (q *queue) Delete(taskId uint64) error { // 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) { - resp, err := q.conn.Call(q.cmds.statistics, []interface{}{q.name}) + resp, err := q.conn.Call17(q.cmds.statistics, []interface{}{q.name}) if err != nil { return nil, err } if len(resp.Data) != 0 { - data, ok := resp.Data[0].([]interface{}) - if ok && len(data) != 0 { - return data[0], nil - } + return resp.Data[0], nil } return nil, nil diff --git a/request.go b/request.go index e672f0b17..df758ef3f 100644 --- a/request.go +++ b/request.go @@ -88,15 +88,26 @@ func (conn *Connection) Upsert(space interface{}, tuple, ops interface{}) (resp } // Call calls registered Tarantool function. -// It uses request code for Tarantool 1.6, so result is converted to array of arrays +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. // // It is equal to conn.CallAsync(functionName, args).Get(). func (conn *Connection) Call(functionName string, args interface{}) (resp *Response, err error) { return conn.CallAsync(functionName, args).Get() } +// Call16 calls registered Tarantool function. +// It uses request code for Tarantool 1.6, so result is converted to array of arrays +// Deprecated since Tarantool 1.7.2. +// +// It is equal to conn.Call16Async(functionName, args).Get(). +func (conn *Connection) Call16(functionName string, args interface{}) (resp *Response, err error) { + return conn.Call16Async(functionName, args).Get() +} + // Call17 calls registered Tarantool function. -// It uses request code for Tarantool 1.7, so result is not converted +// It uses request code for Tarantool >= 1.7, so result is not converted // (though, keep in mind, result is always array) // // It is equal to conn.Call17Async(functionName, args).Get(). @@ -188,15 +199,26 @@ func (conn *Connection) UpdateTyped(space, index interface{}, key, ops interface } // CallTyped calls registered function. -// It uses request code for Tarantool 1.6, so result is converted to array of arrays +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. // -// It is equal to conn.CallAsync(functionName, args).GetTyped(&result). +// It is equal to conn.Call16Async(functionName, args).GetTyped(&result). func (conn *Connection) CallTyped(functionName string, args interface{}, result interface{}) (err error) { return conn.CallAsync(functionName, args).GetTyped(result) } +// Call16Typed calls registered function. +// It uses request code for Tarantool 1.6, so result is converted to array of arrays +// Deprecated since Tarantool 1.7.2. +// +// It is equal to conn.Call16Async(functionName, args).GetTyped(&result). +func (conn *Connection) Call16Typed(functionName string, args interface{}, result interface{}) (err error) { + return conn.Call16Async(functionName, args).GetTyped(result) +} + // Call17Typed calls registered function. -// It uses request code for Tarantool 1.7, so result is not converted +// It uses request code for Tarantool >= 1.7, so result is not converted // (though, keep in mind, result is always array) // // It is equal to conn.Call17Async(functionName, args).GetTyped(&result). @@ -317,7 +339,9 @@ func (conn *Connection) UpsertAsync(space interface{}, tuple interface{}, ops in } // CallAsync sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.6, so future's result is always array of arrays +// It uses request code for Tarantool >= 1.7 if go-tarantool +// was build with go_tarantool_call_17 tag. +// Otherwise, uses request code for Tarantool 1.6. func (conn *Connection) CallAsync(functionName string, args interface{}) *Future { future := conn.newFuture(CallRequest) return conn.sendFuture(future, func(enc *msgpack.Encoder) error { @@ -329,8 +353,22 @@ func (conn *Connection) CallAsync(functionName string, args interface{}) *Future }) } +// Call16Async sends a call to registered Tarantool function and returns Future. +// It uses request code for Tarantool 1.6, so future's result is always array of arrays. +// Deprecated since Tarantool 1.7.2. +func (conn *Connection) Call16Async(functionName string, args interface{}) *Future { + future := conn.newFuture(Call16Request) + return conn.sendFuture(future, func(enc *msgpack.Encoder) error { + enc.EncodeMapLen(2) + enc.EncodeUint64(KeyFunctionName) + enc.EncodeString(functionName) + enc.EncodeUint64(KeyTuple) + return enc.Encode(args) + }) +} + // Call17Async sends a call to registered Tarantool function and returns Future. -// It uses request code for Tarantool 1.7, so future's result will not be converted +// It uses request code for Tarantool >= 1.7, so future's result will not be converted // (though, keep in mind, result is always array) func (conn *Connection) Call17Async(functionName string, args interface{}) *Future { future := conn.newFuture(Call17Request) diff --git a/response.go b/response.go index b7a15706f..80b38849b 100644 --- a/response.go +++ b/response.go @@ -243,7 +243,7 @@ func (resp *Response) String() (str string) { return fmt.Sprintf("<%d ERR 0x%x %s>", resp.RequestId, resp.Code, resp.Error) } -// Tuples converts result of Eval and Call17 to same format +// 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)) diff --git a/tarantool_test.go b/tarantool_test.go index 438f3a18b..9e4c5f7c6 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -722,22 +722,22 @@ func TestClient(t *testing.T) { t.Errorf("Result len of SelectTyped != 1") } - // Call - resp, err = conn.Call("box.info", []interface{}{"box.schema.SPACE_ID"}) + // Call16 + resp, err = conn.Call16("box.info", []interface{}{"box.schema.SPACE_ID"}) if err != nil { - t.Fatalf("Failed to Call: %s", err.Error()) + t.Fatalf("Failed to Call16: %s", err.Error()) } if resp == nil { - t.Fatalf("Response is nil after Call") + t.Fatalf("Response is nil after Call16") } if len(resp.Data) < 1 { t.Errorf("Response.Data is empty after Eval") } - // Call vs Call17 - resp, err = conn.Call("simple_incr", []interface{}{1}) + // Call16 vs Call17 + resp, err = conn.Call16("simple_incr", []interface{}{1}) if err != nil { - t.Errorf("Failed to use Call") + t.Errorf("Failed to use Call16") } if resp.Data[0].([]interface{})[0].(uint64) != 2 { t.Errorf("result is not {{1}} : %v", resp.Data) @@ -745,7 +745,7 @@ func TestClient(t *testing.T) { resp, err = conn.Call17("simple_incr", []interface{}{1}) if err != nil { - t.Errorf("Failed to use Call17") + t.Errorf("Failed to use Call") } if resp.Data[0].(uint64) != 2 { t.Errorf("result is not {{1}} : %v", resp.Data) @@ -803,11 +803,11 @@ func TestClientSessionPush(t *testing.T) { // Future.Get ignores push messages. resp, err := fut1.Get() if err != nil { - t.Errorf("Failed to Call: %s", err.Error()) + 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 { - t.Errorf("Response.Data is empty after CallAsync") + t.Errorf("Response.Data is empty after Call17Async") } else if resp.Data[0].(uint64) != pushMax { t.Errorf("result is not {{1}} : %v", resp.Data) } diff --git a/test_helpers/pool_helper.go b/test_helpers/pool_helper.go index 63f4d09f5..0061870de 100644 --- a/test_helpers/pool_helper.go +++ b/test_helpers/pool_helper.go @@ -195,7 +195,7 @@ func SetInstanceRO(server string, connOpts tarantool.Opts, isReplica bool) error defer conn.Close() - _, err = conn.Call("box.cfg", []interface{}{map[string]bool{"read_only": isReplica}}) + _, err = conn.Call17("box.cfg", []interface{}{map[string]bool{"read_only": isReplica}}) if err != nil { return err }