diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2c49d39..7930b34d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Support errors extended information (#209) - Error type support in MessagePack (#209) - Event subscription support (#119) +- Session settings support (#215) ### Changed diff --git a/Makefile b/Makefile index 59961774b..718139967 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ clean: .PHONY: deps deps: clean - ( cd ./queue; tarantoolctl rocks install queue 1.2.1 ) + ( cd ./queue/testdata; tarantoolctl rocks install queue 1.2.1 ) .PHONY: datetime-timezones datetime-timezones: @@ -81,6 +81,12 @@ test-uuid: go clean -testcache go test -tags "$(TAGS)" ./uuid/ -v -p 1 +.PHONY: test-settings +test-settings: + @echo "Running tests in settings package" + go clean -testcache + go test -tags "$(TAGS)" ./settings/ -v -p 1 + .PHONY: test-main test-main: @echo "Running tests in main package" diff --git a/config.lua b/config.lua index bb976ef43..c8a853ff4 100644 --- a/config.lua +++ b/config.lua @@ -7,7 +7,7 @@ box.cfg{ box.once("init", function() local st = box.schema.space.create('schematest', { - id = 516, + id = 616, temporary = true, if_not_exists = true, field_count = 7, @@ -36,7 +36,7 @@ box.once("init", function() st:truncate() local s = box.schema.space.create('test', { - id = 517, + id = 617, if_not_exists = true, }) s:create_index('primary', { @@ -46,7 +46,7 @@ box.once("init", function() }) local s = box.schema.space.create('teststring', { - id = 518, + id = 618, if_not_exists = true, }) s:create_index('primary', { @@ -56,7 +56,7 @@ box.once("init", function() }) local s = box.schema.space.create('testintint', { - id = 519, + id = 619, if_not_exists = true, }) s:create_index('primary', { @@ -66,7 +66,7 @@ box.once("init", function() }) local s = box.schema.space.create('SQL_TEST', { - id = 520, + id = 620, if_not_exists = true, format = { {name = "NAME0", type = "unsigned"}, @@ -82,7 +82,7 @@ box.once("init", function() s:insert{1, "test", "test"} local s = box.schema.space.create('test_perf', { - id = 521, + id = 621, temporary = true, if_not_exists = true, field_count = 3, @@ -117,7 +117,7 @@ box.once("init", function() end local s = box.schema.space.create('test_error_type', { - id = 522, + id = 622, temporary = true, if_not_exists = true, field_count = 2, diff --git a/connection_pool/connection_pool_test.go b/connection_pool/connection_pool_test.go index 8dc8cc9da..970612b5e 100644 --- a/connection_pool/connection_pool_test.go +++ b/connection_pool/connection_pool_test.go @@ -2379,10 +2379,6 @@ func runTestMain(m *testing.M) int { waitStart := 100 * time.Millisecond connectRetry := 3 retryTimeout := 500 * time.Millisecond - workDirs := []string{ - "work_dir1", "work_dir2", - "work_dir3", "work_dir4", - "work_dir5"} // Tarantool supports streams and interactive transactions since version 2.10.0 isStreamUnsupported, err := test_helpers.IsTarantoolVersionLess(2, 10, 0) @@ -2390,7 +2386,7 @@ func runTestMain(m *testing.M) int { log.Fatalf("Could not check the Tarantool version") } - instances, err = test_helpers.StartTarantoolInstances(servers, workDirs, test_helpers.StartOpts{ + instances, err = test_helpers.StartTarantoolInstances(servers, nil, test_helpers.StartOpts{ InitScript: initScript, User: connOpts.User, Pass: connOpts.Pass, diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 5c63d0f4c..f4cc8b2a1 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -1141,7 +1141,6 @@ func runTestMain(m *testing.M) int { instance, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: "config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond, diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go index 6fba0cc8f..d81e6c488 100644 --- a/decimal/decimal_test.go +++ b/decimal/decimal_test.go @@ -613,7 +613,6 @@ func runTestMain(m *testing.M) int { instance, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: "config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond, diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go index 4087c5620..26d19f3af 100644 --- a/example_custom_unpacking_test.go +++ b/example_custom_unpacking_test.go @@ -87,7 +87,7 @@ func Example_customUnpacking() { log.Fatalf("Failed to connect: %s", err.Error()) } - spaceNo := uint32(517) + spaceNo := uint32(617) indexNo := uint32(0) tuple := Tuple2{Cid: 777, Orig: "orig", Members: []Member{{"lol", "", 1}, {"wut", "", 3}}} diff --git a/example_test.go b/example_test.go index 538fa93b1..27a962da0 100644 --- a/example_test.go +++ b/example_test.go @@ -51,7 +51,7 @@ func ExampleConnection_Select() { conn.Replace(spaceNo, []interface{}{uint(1111), "hello", "world"}) conn.Replace(spaceNo, []interface{}{uint(1112), "hallo", "werld"}) - resp, err := conn.Select(517, 0, 0, 100, tarantool.IterEq, []interface{}{uint(1111)}) + resp, err := conn.Select(617, 0, 0, 100, tarantool.IterEq, []interface{}{uint(1111)}) if err != nil { fmt.Printf("error in select is %v", err) @@ -75,7 +75,7 @@ func ExampleConnection_SelectTyped() { defer conn.Close() var res []Tuple - err := conn.SelectTyped(517, 0, 0, 100, tarantool.IterEq, tarantool.IntKey{1111}, &res) + err := conn.SelectTyped(617, 0, 0, 100, tarantool.IterEq, tarantool.IntKey{1111}, &res) if err != nil { fmt.Printf("error in select is %v", err) @@ -96,7 +96,7 @@ func ExampleConnection_SelectTyped() { func ExampleConnection_SelectAsync() { conn := example_connect(opts) defer conn.Close() - spaceNo := uint32(517) + spaceNo := uint32(617) conn.Insert(spaceNo, []interface{}{uint(16), "test", "one"}) conn.Insert(spaceNo, []interface{}{uint(17), "test", "one"}) @@ -223,7 +223,7 @@ func ExampleSelectRequest() { conn := example_connect(opts) defer conn.Close() - req := tarantool.NewSelectRequest(517). + req := tarantool.NewSelectRequest(617). Limit(100). Key(tarantool.IntKey{1111}) resp, err := conn.Do(req).Get() @@ -253,7 +253,7 @@ func ExampleUpdateRequest() { conn := example_connect(opts) defer conn.Close() - req := tarantool.NewUpdateRequest(517). + req := tarantool.NewUpdateRequest(617). Key(tarantool.IntKey{1111}). Operations(tarantool.NewOperations().Assign(1, "bye")) resp, err := conn.Do(req).Get() @@ -284,7 +284,7 @@ func ExampleUpsertRequest() { defer conn.Close() var req tarantool.Request - req = tarantool.NewUpsertRequest(517). + req = tarantool.NewUpsertRequest(617). Tuple([]interface{}{uint(1113), "first", "first"}). Operations(tarantool.NewOperations().Assign(1, "updated")) resp, err := conn.Do(req).Get() @@ -305,7 +305,7 @@ func ExampleUpsertRequest() { } fmt.Printf("response is %#v\n", resp.Data) - req = tarantool.NewSelectRequest(517). + req = tarantool.NewSelectRequest(617). Limit(100). Key(tarantool.IntKey{1113}) resp, err = conn.Do(req).Get() @@ -830,12 +830,12 @@ func ExampleSchema() { } space1 := schema.Spaces["test"] - space2 := schema.SpacesById[516] + space2 := schema.SpacesById[616] fmt.Printf("Space 1 ID %d %s\n", space1.Id, space1.Name) fmt.Printf("Space 2 ID %d %s\n", space2.Id, space2.Name) // Output: - // Space 1 ID 517 test - // Space 2 ID 516 schematest + // Space 1 ID 617 test + // Space 2 ID 616 schematest } // Example demonstrates how to retrieve information with space schema. @@ -854,7 +854,7 @@ func ExampleSpace() { // Access Space objects by name or ID. space1 := schema.Spaces["test"] - space2 := schema.SpacesById[516] // It's a map. + space2 := schema.SpacesById[616] // It's a map. fmt.Printf("Space 1 ID %d %s %s\n", space1.Id, space1.Name, space1.Engine) fmt.Printf("Space 1 ID %d %t\n", space1.FieldsCount, space1.Temporary) @@ -875,7 +875,7 @@ func ExampleSpace() { fmt.Printf("SpaceField 2 %s %s\n", spaceField2.Name, spaceField2.Type) // Output: - // Space 1 ID 517 test memtx + // Space 1 ID 617 test memtx // Space 1 ID 0 false // Index 0 primary // &{0 unsigned} &{2 string} diff --git a/multi/config.lua b/multi/config.lua index aca4db9b3..7364c0bd6 100644 --- a/multi/config.lua +++ b/multi/config.lua @@ -13,7 +13,7 @@ rawset(_G, 'get_cluster_nodes', get_cluster_nodes) box.once("init", function() local s = box.schema.space.create('test', { - id = 517, + id = 617, if_not_exists = true, }) s:create_index('primary', {type = 'tree', parts = {1, 'string'}, if_not_exists = true}) @@ -22,7 +22,7 @@ box.once("init", function() box.schema.user.grant('test', 'read,write,execute', 'universe') local sp = box.schema.space.create('SQL_TEST', { - id = 521, + id = 621, if_not_exists = true, format = { {name = "NAME0", type = "unsigned"}, diff --git a/multi/multi_test.go b/multi/multi_test.go index ef07d629b..3b395864c 100644 --- a/multi/multi_test.go +++ b/multi/multi_test.go @@ -16,7 +16,7 @@ import ( var server1 = "127.0.0.1:3013" var server2 = "127.0.0.1:3014" -var spaceNo = uint32(517) +var spaceNo = uint32(617) var spaceName = "test" var indexNo = uint32(0) var connOpts = tarantool.Opts{ @@ -592,7 +592,6 @@ func runTestMain(m *testing.M) int { inst1, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: initScript, Listen: server1, - WorkDir: "work_dir1", User: connOpts.User, Pass: connOpts.Pass, WaitStart: waitStart, @@ -609,7 +608,6 @@ func runTestMain(m *testing.M) int { inst2, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: initScript, Listen: server2, - WorkDir: "work_dir2", User: connOpts.User, Pass: connOpts.Pass, WaitStart: waitStart, diff --git a/queue/queue_test.go b/queue/queue_test.go index 85276e4a8..905fefc32 100644 --- a/queue/queue_test.go +++ b/queue/queue_test.go @@ -899,7 +899,6 @@ func runTestMain(m *testing.M) int { inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: "testdata/config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond, @@ -913,7 +912,6 @@ func runTestMain(m *testing.M) int { defer test_helpers.StopTarantoolWithCleanup(inst) - workDirs := []string{"work_dir1", "work_dir2"} poolOpts := test_helpers.StartOpts{ InitScript: "testdata/pool.lua", User: opts.User, @@ -921,7 +919,7 @@ func runTestMain(m *testing.M) int { WaitStart: 3 * time.Second, // replication_timeout * 3 ConnectRetry: -1, } - instances, err = test_helpers.StartTarantoolInstances(serversPool, workDirs, poolOpts) + instances, err = test_helpers.StartTarantoolInstances(serversPool, nil, poolOpts) if err != nil { log.Fatalf("Failed to prepare test tarantool pool: %s", err) diff --git a/queue/testdata/config.lua b/queue/testdata/config.lua index eccb19a68..e0adc069c 100644 --- a/queue/testdata/config.lua +++ b/queue/testdata/config.lua @@ -1,3 +1,28 @@ +-- configure path so that you can run application +-- from outside the root directory +if package.setsearchroot ~= nil then + package.setsearchroot() +else + -- Workaround for rocks loading in tarantool 1.10 + -- It can be removed in tarantool > 2.2 + -- By default, when you do require('mymodule'), tarantool looks into + -- the current working directory and whatever is specified in + -- package.path and package.cpath. If you run your app while in the + -- root directory of that app, everything goes fine, but if you try to + -- start your app with "tarantool myapp/init.lua", it will fail to load + -- its modules, and modules from myapp/.rocks. + local fio = require('fio') + local app_dir = fio.abspath(fio.dirname(arg[0])) + package.path = app_dir .. '/?.lua;' .. package.path + package.path = app_dir .. '/?/init.lua;' .. package.path + package.path = app_dir .. '/.rocks/share/tarantool/?.lua;' .. package.path + package.path = app_dir .. '/.rocks/share/tarantool/?/init.lua;' .. package.path + package.cpath = app_dir .. '/?.so;' .. package.cpath + package.cpath = app_dir .. '/?.dylib;' .. package.cpath + package.cpath = app_dir .. '/.rocks/lib/tarantool/?.so;' .. package.cpath + package.cpath = app_dir .. '/.rocks/lib/tarantool/?.dylib;' .. package.cpath +end + local queue = require('queue') rawset(_G, 'queue', queue) diff --git a/queue/testdata/pool.lua b/queue/testdata/pool.lua index 7c63aa787..1bcc11654 100644 --- a/queue/testdata/pool.lua +++ b/queue/testdata/pool.lua @@ -1,3 +1,28 @@ +-- configure path so that you can run application +-- from outside the root directory +if package.setsearchroot ~= nil then + package.setsearchroot() +else + -- Workaround for rocks loading in tarantool 1.10 + -- It can be removed in tarantool > 2.2 + -- By default, when you do require('mymodule'), tarantool looks into + -- the current working directory and whatever is specified in + -- package.path and package.cpath. If you run your app while in the + -- root directory of that app, everything goes fine, but if you try to + -- start your app with "tarantool myapp/init.lua", it will fail to load + -- its modules, and modules from myapp/.rocks. + local fio = require('fio') + local app_dir = fio.abspath(fio.dirname(arg[0])) + package.path = app_dir .. '/?.lua;' .. package.path + package.path = app_dir .. '/?/init.lua;' .. package.path + package.path = app_dir .. '/.rocks/share/tarantool/?.lua;' .. package.path + package.path = app_dir .. '/.rocks/share/tarantool/?/init.lua;' .. package.path + package.cpath = app_dir .. '/?.so;' .. package.cpath + package.cpath = app_dir .. '/?.dylib;' .. package.cpath + package.cpath = app_dir .. '/.rocks/lib/tarantool/?.so;' .. package.cpath + package.cpath = app_dir .. '/.rocks/lib/tarantool/?.dylib;' .. package.cpath +end + local queue = require('queue') rawset(_G, 'queue', queue) diff --git a/settings/const.go b/settings/const.go new file mode 100644 index 000000000..cc980cd7a --- /dev/null +++ b/settings/const.go @@ -0,0 +1,21 @@ +package settings + +const sessionSettingsSpace string = "_session_settings" + +// In Go and IPROTO_UPDATE count starts with 0. +const sessionSettingValueField int = 1 + +const ( + errorMarshalingEnabled string = "error_marshaling_enabled" + sqlDefaultEngine string = "sql_default_engine" + sqlDeferForeignKeys string = "sql_defer_foreign_keys" + sqlFullColumnNames string = "sql_full_column_names" + sqlFullMetadata string = "sql_full_metadata" + sqlParserDebug string = "sql_parser_debug" + sqlRecursiveTriggers string = "sql_recursive_triggers" + sqlReverseUnorderedSelects string = "sql_reverse_unordered_selects" + sqlSelectDebug string = "sql_select_debug" + sqlVDBEDebug string = "sql_vdbe_debug" +) + +const selectAllLimit uint32 = 1000 diff --git a/settings/example_test.go b/settings/example_test.go new file mode 100644 index 000000000..a2391328f --- /dev/null +++ b/settings/example_test.go @@ -0,0 +1,78 @@ +package settings_test + +import ( + "fmt" + + "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/settings" + "github.com/tarantool/go-tarantool/test_helpers" +) + +func example_connect(opts tarantool.Opts) *tarantool.Connection { + conn, err := tarantool.Connect(server, opts) + if err != nil { + panic("Connection is not established: " + err.Error()) + } + return conn +} + +func Example_sqlFullColumnNames() { + var resp *tarantool.Response + var err error + var isLess bool + + conn := example_connect(opts) + defer conn.Close() + + // Tarantool supports session settings since version 2.3.1 + isLess, err = test_helpers.IsTarantoolVersionLess(2, 3, 1) + if err != nil || isLess { + return + } + + // Create a space. + _, err = conn.Execute("CREATE TABLE example(id INT PRIMARY KEY, x INT);", []interface{}{}) + if err != nil { + fmt.Printf("error in create table: %v\n", err) + return + } + + // Insert some tuple into space. + _, err = conn.Execute("INSERT INTO example VALUES (1, 1);", []interface{}{}) + if err != nil { + fmt.Printf("error on insert: %v\n", err) + return + } + + // Enable showing full column names in SQL responses. + _, err = conn.Do(settings.NewSQLFullColumnNamesSetRequest(true)).Get() + if err != nil { + fmt.Printf("error on setting setup: %v\n", err) + return + } + + // Get some data with SQL query. + resp, err = conn.Execute("SELECT x FROM example WHERE id = 1;", []interface{}{}) + if err != nil { + fmt.Printf("error on select: %v\n", err) + return + } + // Show response metadata. + fmt.Printf("full column name: %v\n", resp.MetaData[0].FieldName) + + // Disable showing full column names in SQL responses. + _, err = conn.Do(settings.NewSQLFullColumnNamesSetRequest(false)).Get() + if err != nil { + fmt.Printf("error on setting setup: %v\n", err) + return + } + + // Get some data with SQL query. + resp, err = conn.Execute("SELECT x FROM example WHERE id = 1;", []interface{}{}) + if err != nil { + fmt.Printf("error on select: %v\n", err) + return + } + // Show response metadata. + fmt.Printf("short column name: %v\n", resp.MetaData[0].FieldName) +} diff --git a/settings/msgpack.go b/settings/msgpack.go new file mode 100644 index 000000000..295620aba --- /dev/null +++ b/settings/msgpack.go @@ -0,0 +1,10 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package settings + +import ( + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder diff --git a/settings/msgpack_helper_test.go b/settings/msgpack_helper_test.go new file mode 100644 index 000000000..0c002213a --- /dev/null +++ b/settings/msgpack_helper_test.go @@ -0,0 +1,22 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package settings_test + +import ( + "io" + + "github.com/tarantool/go-tarantool" + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder + +func NewEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} + +func toBoxError(i interface{}) (v tarantool.BoxError, ok bool) { + v, ok = i.(tarantool.BoxError) + return +} diff --git a/settings/msgpack_v5.go b/settings/msgpack_v5.go new file mode 100644 index 000000000..288418ec6 --- /dev/null +++ b/settings/msgpack_v5.go @@ -0,0 +1,10 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package settings + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder diff --git a/settings/msgpack_v5_helper_test.go b/settings/msgpack_v5_helper_test.go new file mode 100644 index 000000000..96df6bae1 --- /dev/null +++ b/settings/msgpack_v5_helper_test.go @@ -0,0 +1,25 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package settings_test + +import ( + "io" + + "github.com/tarantool/go-tarantool" + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder + +func NewEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} + +func toBoxError(i interface{}) (v tarantool.BoxError, ok bool) { + var ptr *tarantool.BoxError + if ptr, ok = i.(*tarantool.BoxError); ok { + v = *ptr + } + return +} diff --git a/settings/request.go b/settings/request.go new file mode 100644 index 000000000..84723bb2d --- /dev/null +++ b/settings/request.go @@ -0,0 +1,273 @@ +// Package settings is a collection of requests to set a connection session setting +// or get current session configuration. +// +// +============================+=========================+=========+===========================+ +// | Setting | Meaning | Default | Supported in | +// | | | | Tarantool versions | +// +============================+=========================+=========+===========================+ +// | ErrorMarshalingEnabled | Defines whether error | false | Since 2.4.1 till 2.10.0, | +// | | objectshave a special | | replaced with IPROTO_ID | +// | | structure. | | feature flag. | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLDefaultEngine | Defines default storage | "memtx" | Since 2.3.1. | +// | | engine for new SQL | | | +// | | tables. | | | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLDeferForeignKeys | Defines whether | false | Since 2.3.1 till master | +// | | foreign-key checks can | | commit 14618c4 (possible | +// | | wait till commit. | | 2.10.5 or 2.11.0) | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLFullColumnNames | Defines whether full | false | Since 2.3.1. | +// | | column names is | | | +// | | displayed in SQL result | | | +// | | set metadata. | | | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLFullMetadata | Defines whether SQL | false | Since 2.3.1. | +// | | result set metadata | | | +// | | will have more than | | | +// | | just name and type. | | | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLParserDebug | Defines whether to show | false | Since 2.3.1 (only if | +// | | parser steps for | | built with | +// | | following statements. | | -DCMAKE_BUILD_TYPE=Debug) | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLRecursiveTriggers | Defines whether a | true | Since 2.3.1. | +// | | triggered statement can | | | +// | | activate a trigger. | | | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLReverseUnorderedSelects | Defines defines whether | false | Since 2.3.1. | +// | | result rows are usually | | | +// | | in reverse order if | | | +// | | there is no ORDER BY | | | +// | | clause. | | | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLSelectDebug | Defines whether to show | false | Since 2.3.1 (only if | +// | | to show execution steps | | built with | +// | | during SELECT. | | -DCMAKE_BUILD_TYPE=Debug) | +// +----------------------------+-------------------------+---------+---------------------------+ +// | SQLVDBEDebug | Defines whether VDBE | false | Since 2.3.1 (only if | +// | | debug mode is enabled. | | built with | +// | | | | -DCMAKE_BUILD_TYPE=Debug) | +// +----------------------------+-------------------------+---------+---------------------------+ +// +// Since: 1.10.0 +// +// See also: +// +// * Session settings https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/_session_settings/ +package settings + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// SetRequest helps to set session settings. +type SetRequest struct { + impl *tarantool.UpdateRequest +} + +func newSetRequest(setting string, value interface{}) *SetRequest { + return &SetRequest{ + impl: tarantool.NewUpdateRequest(sessionSettingsSpace). + Key(tarantool.StringKey{S: setting}). + Operations(tarantool.NewOperations().Assign(sessionSettingValueField, value)), + } +} + +// Context sets a passed context to set session settings request. +func (req *SetRequest) Context(ctx context.Context) *SetRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// Code returns IPROTO code for set session settings request. +func (req *SetRequest) Code() int32 { + return req.impl.Code() +} + +// Body fills an encoder with set session settings request body. +func (req *SetRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + return req.impl.Body(res, enc) +} + +// Ctx returns a context of set session settings request. +func (req *SetRequest) Ctx() context.Context { + return req.impl.Ctx() +} + +// Async returns is set session settings request expects a response. +func (req *SetRequest) Async() bool { + return req.impl.Async() +} + +// GetRequest helps to get session settings. +type GetRequest struct { + impl *tarantool.SelectRequest +} + +func newGetRequest(setting string) *GetRequest { + return &GetRequest{ + impl: tarantool.NewSelectRequest(sessionSettingsSpace). + Key(tarantool.StringKey{S: setting}). + Limit(1), + } +} + +// Context sets a passed context to get session settings request. +func (req *GetRequest) Context(ctx context.Context) *GetRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// Code returns IPROTO code for get session settings request. +func (req *GetRequest) Code() int32 { + return req.impl.Code() +} + +// Body fills an encoder with get session settings request body. +func (req *GetRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + return req.impl.Body(res, enc) +} + +// Ctx returns a context of get session settings request. +func (req *GetRequest) Ctx() context.Context { + return req.impl.Ctx() +} + +// Async returns is get session settings request expects a response. +func (req *GetRequest) Async() bool { + return req.impl.Async() +} + +// NewErrorMarshalingEnabledSetRequest creates a request to +// update current session ErrorMarshalingEnabled setting. +func NewErrorMarshalingEnabledSetRequest(value bool) *SetRequest { + return newSetRequest(errorMarshalingEnabled, value) +} + +// NewErrorMarshalingEnabledGetRequest creates a request to get +// current session ErrorMarshalingEnabled setting in tuple format. +func NewErrorMarshalingEnabledGetRequest() *GetRequest { + return newGetRequest(errorMarshalingEnabled) +} + +// NewSQLDefaultEngineSetRequest creates a request to +// update current session SQLDefaultEngine setting. +func NewSQLDefaultEngineSetRequest(value string) *SetRequest { + return newSetRequest(sqlDefaultEngine, value) +} + +// NewSQLDefaultEngineGetRequest creates a request to get +// current session SQLDefaultEngine setting in tuple format. +func NewSQLDefaultEngineGetRequest() *GetRequest { + return newGetRequest(sqlDefaultEngine) +} + +// NewSQLDeferForeignKeysSetRequest creates a request to +// update current session SQLDeferForeignKeys setting. +func NewSQLDeferForeignKeysSetRequest(value bool) *SetRequest { + return newSetRequest(sqlDeferForeignKeys, value) +} + +// NewSQLDeferForeignKeysGetRequest creates a request to get +// current session SQLDeferForeignKeys setting in tuple format. +func NewSQLDeferForeignKeysGetRequest() *GetRequest { + return newGetRequest(sqlDeferForeignKeys) +} + +// NewSQLFullColumnNamesSetRequest creates a request to +// update current session SQLFullColumnNames setting. +func NewSQLFullColumnNamesSetRequest(value bool) *SetRequest { + return newSetRequest(sqlFullColumnNames, value) +} + +// NewSQLFullColumnNamesGetRequest creates a request to get +// current session SQLFullColumnNames setting in tuple format. +func NewSQLFullColumnNamesGetRequest() *GetRequest { + return newGetRequest(sqlFullColumnNames) +} + +// NewSQLFullMetadataSetRequest creates a request to +// update current session SQLFullMetadata setting. +func NewSQLFullMetadataSetRequest(value bool) *SetRequest { + return newSetRequest(sqlFullMetadata, value) +} + +// NewSQLFullMetadataGetRequest creates a request to get +// current session SQLFullMetadata setting in tuple format. +func NewSQLFullMetadataGetRequest() *GetRequest { + return newGetRequest(sqlFullMetadata) +} + +// NewSQLParserDebugSetRequest creates a request to +// update current session SQLParserDebug setting. +func NewSQLParserDebugSetRequest(value bool) *SetRequest { + return newSetRequest(sqlParserDebug, value) +} + +// NewSQLParserDebugGetRequest creates a request to get +// current session SQLParserDebug setting in tuple format. +func NewSQLParserDebugGetRequest() *GetRequest { + return newGetRequest(sqlParserDebug) +} + +// NewSQLRecursiveTriggersSetRequest creates a request to +// update current session SQLRecursiveTriggers setting. +func NewSQLRecursiveTriggersSetRequest(value bool) *SetRequest { + return newSetRequest(sqlRecursiveTriggers, value) +} + +// NewSQLRecursiveTriggersGetRequest creates a request to get +// current session SQLRecursiveTriggers setting in tuple format. +func NewSQLRecursiveTriggersGetRequest() *GetRequest { + return newGetRequest(sqlRecursiveTriggers) +} + +// NewSQLReverseUnorderedSelectsSetRequest creates a request to +// update current session SQLReverseUnorderedSelects setting. +func NewSQLReverseUnorderedSelectsSetRequest(value bool) *SetRequest { + return newSetRequest(sqlReverseUnorderedSelects, value) +} + +// NewSQLReverseUnorderedSelectsGetRequest creates a request to get +// current session SQLReverseUnorderedSelects setting in tuple format. +func NewSQLReverseUnorderedSelectsGetRequest() *GetRequest { + return newGetRequest(sqlReverseUnorderedSelects) +} + +// NewSQLSelectDebugSetRequest creates a request to +// update current session SQLSelectDebug setting. +func NewSQLSelectDebugSetRequest(value bool) *SetRequest { + return newSetRequest(sqlSelectDebug, value) +} + +// NewSQLSelectDebugGetRequest creates a request to get +// current session SQLSelectDebug setting in tuple format. +func NewSQLSelectDebugGetRequest() *GetRequest { + return newGetRequest(sqlSelectDebug) +} + +// NewSQLVDBEDebugSetRequest creates a request to +// update current session SQLVDBEDebug setting. +func NewSQLVDBEDebugSetRequest(value bool) *SetRequest { + return newSetRequest(sqlVDBEDebug, value) +} + +// NewSQLVDBEDebugGetRequest creates a request to get +// current session SQLVDBEDebug setting in tuple format. +func NewSQLVDBEDebugGetRequest() *GetRequest { + return newGetRequest(sqlVDBEDebug) +} + +// NewSessionSettingsGetRequest creates a request to get all +// current session settings in tuple format. +func NewSessionSettingsGetRequest() *GetRequest { + return &GetRequest{ + impl: tarantool.NewSelectRequest(sessionSettingsSpace). + Limit(selectAllLimit), + } +} diff --git a/settings/request_test.go b/settings/request_test.go new file mode 100644 index 000000000..bc26ff058 --- /dev/null +++ b/settings/request_test.go @@ -0,0 +1,118 @@ +package settings_test + +import ( + "bytes" + "context" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool" + . "github.com/tarantool/go-tarantool/settings" +) + +type ValidSchemeResolver struct { +} + +func (*ValidSchemeResolver) ResolveSpaceIndex(s, i interface{}) (spaceNo, indexNo uint32, err error) { + if s == nil { + if s == "_session_settings" { + spaceNo = 380 + } else { + spaceNo = uint32(s.(int)) + } + } else { + spaceNo = 0 + } + if i != nil { + indexNo = uint32(i.(int)) + } else { + indexNo = 0 + } + + return spaceNo, indexNo, nil +} + +var resolver ValidSchemeResolver + +func TestRequestsAPI(t *testing.T) { + tests := []struct { + req tarantool.Request + async bool + code int32 + }{ + {req: NewErrorMarshalingEnabledSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewErrorMarshalingEnabledGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLDefaultEngineSetRequest("memtx"), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLDefaultEngineGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLDeferForeignKeysSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLDeferForeignKeysGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLFullColumnNamesSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLFullColumnNamesGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLFullMetadataSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLFullMetadataGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLParserDebugSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLParserDebugGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLRecursiveTriggersSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLRecursiveTriggersGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLReverseUnorderedSelectsSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLReverseUnorderedSelectsGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLSelectDebugSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLSelectDebugGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSQLVDBEDebugSetRequest(false), async: false, code: tarantool.UpdateRequestCode}, + {req: NewSQLVDBEDebugGetRequest(), async: false, code: tarantool.SelectRequestCode}, + {req: NewSessionSettingsGetRequest(), async: false, code: tarantool.SelectRequestCode}, + } + + for _, test := range tests { + require.Equal(t, test.async, test.req.Async()) + require.Equal(t, test.code, test.req.Code()) + + var reqBuf bytes.Buffer + enc := NewEncoder(&reqBuf) + require.Nilf(t, test.req.Body(&resolver, enc), "No errors on fill") + } +} + +func TestRequestsCtx(t *testing.T) { + // tarantool.Request interface doesn't have Context() + getTests := []struct { + req *GetRequest + }{ + {req: NewErrorMarshalingEnabledGetRequest()}, + {req: NewSQLDefaultEngineGetRequest()}, + {req: NewSQLDeferForeignKeysGetRequest()}, + {req: NewSQLFullColumnNamesGetRequest()}, + {req: NewSQLFullMetadataGetRequest()}, + {req: NewSQLParserDebugGetRequest()}, + {req: NewSQLRecursiveTriggersGetRequest()}, + {req: NewSQLReverseUnorderedSelectsGetRequest()}, + {req: NewSQLSelectDebugGetRequest()}, + {req: NewSQLVDBEDebugGetRequest()}, + {req: NewSessionSettingsGetRequest()}, + } + + for _, test := range getTests { + var ctx context.Context + require.Equal(t, ctx, test.req.Context(ctx).Ctx()) + } + + setTests := []struct { + req *SetRequest + }{ + {req: NewErrorMarshalingEnabledSetRequest(false)}, + {req: NewSQLDefaultEngineSetRequest("memtx")}, + {req: NewSQLDeferForeignKeysSetRequest(false)}, + {req: NewSQLFullColumnNamesSetRequest(false)}, + {req: NewSQLFullMetadataSetRequest(false)}, + {req: NewSQLParserDebugSetRequest(false)}, + {req: NewSQLRecursiveTriggersSetRequest(false)}, + {req: NewSQLReverseUnorderedSelectsSetRequest(false)}, + {req: NewSQLSelectDebugSetRequest(false)}, + {req: NewSQLVDBEDebugSetRequest(false)}, + } + + for _, test := range setTests { + var ctx context.Context + require.Equal(t, ctx, test.req.Context(ctx).Ctx()) + } +} diff --git a/settings/tarantool_test.go b/settings/tarantool_test.go new file mode 100644 index 000000000..d32767116 --- /dev/null +++ b/settings/tarantool_test.go @@ -0,0 +1,656 @@ +package settings_test + +import ( + "log" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool" + . "github.com/tarantool/go-tarantool/settings" + "github.com/tarantool/go-tarantool/test_helpers" +) + +// There is no way to skip tests in testing.M, +// so we use this variable to pass info +// to each testing.T that it should skip. +var isSettingsSupported = false + +var server = "127.0.0.1:3013" +var opts = tarantool.Opts{ + Timeout: 500 * time.Millisecond, + User: "test", + Pass: "test", +} + +func skipIfSettingsUnsupported(t *testing.T) { + t.Helper() + + if isSettingsSupported == false { + t.Skip("Skipping test for Tarantool without session settings support") + } +} + +func skipIfErrorMarshalingEnabledSettingUnsupported(t *testing.T) { + t.Helper() + + test_helpers.SkipIfFeatureUnsupported(t, "error_marshaling_enabled session setting", 2, 4, 1) + test_helpers.SkipIfFeatureDropped(t, "error_marshaling_enabled session setting", 2, 10, 0) +} + +func skipIfSQLDeferForeignKeysSettingUnsupported(t *testing.T) { + t.Helper() + + test_helpers.SkipIfFeatureUnsupported(t, "sql_defer_foreign_keys session setting", 2, 3, 1) + test_helpers.SkipIfFeatureDropped(t, "sql_defer_foreign_keys session setting", 2, 10, 5) +} + +func TestErrorMarshalingEnabledSetting(t *testing.T) { + skipIfErrorMarshalingEnabledSettingUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Disable receiving box.error as MP_EXT 3. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", false}}, resp.Data) + + // Get a box.Error value. + resp, err = conn.Eval("return box.error.new(box.error.UNKNOWN)", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.IsType(t, "string", resp.Data[0]) + + // Enable receiving box.error as MP_EXT 3. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewErrorMarshalingEnabledGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"error_marshaling_enabled", true}}, resp.Data) + + // Get a box.Error value. + resp, err = conn.Eval("return box.error.new(box.error.UNKNOWN)", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + _, ok := toBoxError(resp.Data[0]) + require.True(t, ok) +} + +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, server, opts) + defer conn.Close() + + // Set default SQL "CREATE TABLE" engine to "vinyl". + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "vinyl"}}, resp.Data) + + // Create a space with "CREATE TABLE". + resp, err = conn.Execute("CREATE TABLE t1_vinyl(a INT PRIMARY KEY, b INT, c INT);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Check new space engine. + resp, err = conn.Eval("return box.space['T1_VINYL'].engine", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, "vinyl", resp.Data[0]) + + // Set default SQL "CREATE TABLE" engine to "memtx". + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLDefaultEngineGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_default_engine", "memtx"}}, resp.Data) + + // Create a space with "CREATE TABLE". + resp, err = conn.Execute("CREATE TABLE t2_memtx(a INT PRIMARY KEY, b INT, c INT);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Check new space engine. + resp, err = conn.Eval("return box.space['T2_MEMTX'].engine", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, "memtx", resp.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 err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Create a parent space. + resp, err = conn.Execute("CREATE TABLE parent(id INT PRIMARY KEY, y INT UNIQUE);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Create a space with reference to the parent space. + resp, err = conn.Execute("CREATE TABLE child(id INT PRIMARY KEY, x INT REFERENCES parent(y));", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + deferEval := ` + box.begin() + local _, err = box.execute('INSERT INTO child VALUES (2, 2);') + if err ~= nil then + box.rollback() + error(err) + end + box.execute('INSERT INTO parent VALUES (2, 2);') + box.commit() + return true + ` + + // Disable foreign key constraint checks before commit. + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Evaluate a scenario when foreign key not exists + // on INSERT, but exists on commit. + _, err = conn.Eval(deferEval, []interface{}{}) + require.NotNil(t, err) + require.ErrorContains(t, err, "Failed to execute SQL statement: FOREIGN KEY constraint failed") + + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Evaluate a scenario when foreign key not exists + // on INSERT, but exists on commit. + resp, err = conn.Eval(deferEval, []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, true, resp.Data[0]) +} + +func TestSQLFullColumnNamesSetting(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Create a space. + resp, err = conn.Execute("CREATE TABLE fkname(id INT PRIMARY KEY, x INT);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Fill it with some data. + resp, err = conn.Execute("INSERT INTO fkname VALUES (1, 1);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Disable displaying full column names in metadata. + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Get a data with short column names in metadata. + resp, err = conn.Execute("SELECT x FROM fkname WHERE id = 1;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, "X", resp.MetaData[0].FieldName) + + // Enable displaying full column names in metadata. + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Get a data with full column names in metadata. + resp, err = conn.Execute("SELECT x FROM fkname WHERE id = 1;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, "FKNAME.X", resp.MetaData[0].FieldName) +} + +func TestSQLFullMetadataSetting(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Create a space. + resp, err = conn.Execute("CREATE TABLE fmt(id INT PRIMARY KEY, x INT);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Fill it with some data. + resp, err = conn.Execute("INSERT INTO fmt VALUES (1, 1);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Disable displaying additional fields in metadata. + resp, err = conn.Do(NewSQLFullMetadataSetRequest(false)).Get() + require.Nil(t, err) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, resp.Data) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", false}}, resp.Data) + + // Get a data without additional fields in metadata. + resp, err = conn.Execute("SELECT x FROM fmt WHERE id = 1;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, "", resp.MetaData[0].FieldSpan) + + // Enable displaying full column names in metadata. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLFullMetadataGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_full_metadata", true}}, resp.Data) + + // Get a data with additional fields in metadata. + resp, err = conn.Execute("SELECT x FROM fmt WHERE id = 1;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + 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, server, opts) + defer conn.Close() + + // Disable parser debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLParserDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", false}}, resp.Data) + + // Enable parser debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLParserDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_parser_debug", true}}, resp.Data) + + // To test real effect we need a Tarantool instance built with + // `-DCMAKE_BUILD_TYPE=Debug`. +} + +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 err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Create a space. + resp, err = conn.Execute("CREATE TABLE rec(id INTEGER PRIMARY KEY, a INT, b INT);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Fill it with some data. + resp, err = conn.Execute("INSERT INTO rec VALUES(1, 1, 2);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Create a recursive trigger (with infinite depth). + resp, err = conn.Execute(` + CREATE TRIGGER tr12 AFTER UPDATE ON rec FOR EACH ROW BEGIN + UPDATE rec SET a=new.a+1, b=new.b+1; + END;`, []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Enable SQL recursive triggers. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", true}}, resp.Data) + + // Trigger the recursion. + _, err = conn.Execute("UPDATE rec SET a=a+1, b=b+1;", []interface{}{}) + require.NotNil(t, err) + require.ErrorContains(t, err, "Failed to execute SQL statement: too many levels of trigger recursion") + + // Disable SQL recursive triggers. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLRecursiveTriggersGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_recursive_triggers", false}}, resp.Data) + + // Trigger the recursion. + resp, err = conn.Execute("UPDATE rec SET a=a+1, b=b+1;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) +} + +func TestSQLReverseUnorderedSelectsSetting(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Create a space. + resp, err = conn.Execute("CREATE TABLE data(id STRING PRIMARY KEY);", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Fill it with some data. + resp, err = conn.Execute("INSERT INTO data VALUES('1');", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + resp, err = conn.Execute("INSERT INTO data VALUES('2');", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, uint64(1), resp.SQLInfo.AffectedCount) + + // Disable reverse order in unordered selects. + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Select multiple records. + resp, err = conn.Execute("SELECT * FROM data;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.EqualValues(t, []interface{}{"1"}, resp.Data[0]) + require.EqualValues(t, []interface{}{"2"}, resp.Data[1]) + + // Enable reverse order in unordered selects. + resp, 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) + + // Fetch current setting value. + resp, 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) + + // Select multiple records. + resp, err = conn.Execute("SELECT * FROM data;", []interface{}{}) + require.Nil(t, err) + require.NotNil(t, resp) + require.EqualValues(t, []interface{}{"2"}, resp.Data[0]) + require.EqualValues(t, []interface{}{"1"}, resp.Data[1]) +} + +func TestSQLSelectDebugSetting(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Disable select debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", false}}, resp.Data) + + // Enable select debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLSelectDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_select_debug", true}}, resp.Data) + + // To test real effect we need a Tarantool instance built with + // `-DCMAKE_BUILD_TYPE=Debug`. +} + +func TestSQLVDBEDebugSetting(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Disable VDBE debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", false}}, resp.Data) + + // Enable VDBE debug mode. + resp, 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) + + // Fetch current setting value. + resp, err = conn.Do(NewSQLVDBEDebugGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Equal(t, []interface{}{[]interface{}{"sql_vdbe_debug", true}}, resp.Data) + + // To test real effect we need a Tarantool instance built with + // `-DCMAKE_BUILD_TYPE=Debug`. +} + +func TestSessionSettings(t *testing.T) { + skipIfSettingsUnsupported(t) + + var resp *tarantool.Response + var err error + + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + // Set some settings values. + resp, 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) + + resp, 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) + + // Fetch current settings values. + resp, err = conn.Do(NewSessionSettingsGetRequest()).Get() + require.Nil(t, err) + require.NotNil(t, resp) + require.Subset(t, resp.Data, + []interface{}{ + []interface{}{"sql_default_engine", "memtx"}, + []interface{}{"sql_full_column_names", true}, + }) +} + +// 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 +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + isLess, err := test_helpers.IsTarantoolVersionLess(2, 3, 1) + if err != nil { + log.Fatalf("Failed to extract tarantool version: %s", err) + } + + if isLess { + log.Println("Skipping session settings tests...") + isSettingsSupported = false + return m.Run() + } + + isSettingsSupported = true + + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "testdata/config.lua", + Listen: server, + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 500 * time.Millisecond, + }) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + defer test_helpers.StopTarantoolWithCleanup(inst) + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/settings/testdata/config.lua b/settings/testdata/config.lua new file mode 100644 index 000000000..7f6af1db2 --- /dev/null +++ b/settings/testdata/config.lua @@ -0,0 +1,15 @@ +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. +box.cfg{ + work_dir = os.getenv("TEST_TNT_WORK_DIR"), +} + +box.schema.user.create('test', { password = 'test' , if_not_exists = true }) +box.schema.user.grant('test', 'execute', 'universe', nil, { if_not_exists = true }) +box.schema.user.grant('test', 'create,read,write,drop,alter', 'space', nil, { if_not_exists = true }) +box.schema.user.grant('test', 'create', 'sequence', nil, { if_not_exists = true }) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/ssl_test.go b/ssl_test.go index 36ce4905f..769508284 100644 --- a/ssl_test.go +++ b/ssl_test.go @@ -126,7 +126,6 @@ func serverTnt(serverOpts, clientOpts SslOpts) (test_helpers.TarantoolInstance, ClientServer: tntHost, ClientTransport: "ssl", ClientSsl: clientOpts, - WorkDir: "work_dir_ssl", User: "test", Pass: "test", WaitStart: 100 * time.Millisecond, diff --git a/tarantool_test.go b/tarantool_test.go index de7af9e91..96bf02254 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -23,7 +23,6 @@ import ( var startOpts test_helpers.StartOpts = test_helpers.StartOpts{ InitScript: "config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond, @@ -99,7 +98,7 @@ func convertUint64(v interface{}) (result uint64, err error) { } var server = "127.0.0.1:3013" -var spaceNo = uint32(517) +var spaceNo = uint32(617) var spaceName = "test" var indexNo = uint32(0) var indexName = "primary" @@ -1776,29 +1775,29 @@ func TestSchema(t *testing.T) { } var space, space2 *Space var ok bool - if space, ok = schema.SpacesById[516]; !ok { - t.Errorf("space with id = 516 was not found in schema.SpacesById") + if space, ok = schema.SpacesById[616]; !ok { + t.Errorf("space with id = 616 was not found in schema.SpacesById") } if space2, ok = schema.Spaces["schematest"]; !ok { t.Errorf("space with name 'schematest' was not found in schema.SpacesById") } if space != space2 { - t.Errorf("space with id = 516 and space with name schematest are different") + t.Errorf("space with id = 616 and space with name schematest are different") } - if space.Id != 516 { - t.Errorf("space 516 has incorrect Id") + if space.Id != 616 { + t.Errorf("space 616 has incorrect Id") } if space.Name != "schematest" { - t.Errorf("space 516 has incorrect Name") + t.Errorf("space 616 has incorrect Name") } if !space.Temporary { - t.Errorf("space 516 should be temporary") + t.Errorf("space 616 should be temporary") } if space.Engine != "memtx" { - t.Errorf("space 516 engine should be memtx") + t.Errorf("space 616 engine should be memtx") } if space.FieldsCount != 7 { - t.Errorf("space 516 has incorrect fields count") + t.Errorf("space 616 has incorrect fields count") } if space.FieldsById == nil { @@ -1908,20 +1907,20 @@ func TestSchema(t *testing.T) { } var rSpaceNo, rIndexNo uint32 - rSpaceNo, rIndexNo, err = schema.ResolveSpaceIndex(516, 3) - if err != nil || rSpaceNo != 516 || rIndexNo != 3 { + rSpaceNo, rIndexNo, err = schema.ResolveSpaceIndex(616, 3) + if err != nil || rSpaceNo != 616 || rIndexNo != 3 { t.Errorf("numeric space and index params not resolved as-is") } - rSpaceNo, _, err = schema.ResolveSpaceIndex(516, nil) - if err != nil || rSpaceNo != 516 { + rSpaceNo, _, err = schema.ResolveSpaceIndex(616, nil) + if err != nil || rSpaceNo != 616 { t.Errorf("numeric space param not resolved as-is") } rSpaceNo, rIndexNo, err = schema.ResolveSpaceIndex("schematest", "secondary") - if err != nil || rSpaceNo != 516 || rIndexNo != 3 { + if err != nil || rSpaceNo != 616 || rIndexNo != 3 { t.Errorf("symbolic space and index params not resolved") } rSpaceNo, _, err = schema.ResolveSpaceIndex("schematest", nil) - if err != nil || rSpaceNo != 516 { + if err != nil || rSpaceNo != 616 { t.Errorf("symbolic space param not resolved") } _, _, err = schema.ResolveSpaceIndex("schematest22", "secondary") @@ -3317,7 +3316,6 @@ func TestConnection_NewWatcher_reconnect(t *testing.T) { inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: "config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond, diff --git a/test_helpers/main.go b/test_helpers/main.go index 77e22c535..f6e745617 100644 --- a/test_helpers/main.go +++ b/test_helpers/main.go @@ -47,9 +47,10 @@ type StartOpts struct { // a Tarantool instance. ClientSsl tarantool.SslOpts - // WorkDir is box.cfg work_dir parameter for tarantool. - // Specify folder to store tarantool data files. - // Folder must be unique for each tarantool process used simultaneously. + // WorkDir is box.cfg work_dir parameter for a Tarantool instance: + // a folder to store data files. If not specified, helpers create a + // new temporary directory. + // Folder must be unique for each Tarantool process used simultaneously. // https://www.tarantool.io/en/doc/latest/reference/configuration/#confval-work_dir WorkDir string @@ -189,6 +190,32 @@ func RestartTarantool(inst *TarantoolInstance) error { func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) { // Prepare tarantool command. var inst TarantoolInstance + var dir string + var err error + + if startOpts.WorkDir == "" { + // Create work_dir for a new instance. + // TO DO: replace with `os.MkdirTemp` when we drop support of + // Go 1.16 an older + dir, err = ioutil.TempDir("", "work_dir") + if err != nil { + return inst, err + } + startOpts.WorkDir = dir + } else { + // Clean up existing work_dir. + err = os.RemoveAll(startOpts.WorkDir) + if err != nil { + return inst, err + } + + // Create work_dir. + err = os.Mkdir(startOpts.WorkDir, 0755) + if err != nil { + return inst, err + } + } + inst.Cmd = exec.Command("tarantool", startOpts.InitScript) inst.Cmd.Env = append( @@ -198,18 +225,6 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) { fmt.Sprintf("TEST_TNT_MEMTX_USE_MVCC_ENGINE=%t", startOpts.MemtxUseMvccEngine), ) - // Clean up existing work_dir. - err := os.RemoveAll(startOpts.WorkDir) - if err != nil { - return inst, err - } - - // Create work_dir. - err = os.Mkdir(startOpts.WorkDir, 0755) - if err != nil { - return inst, err - } - // Copy SSL certificates. if startOpts.SslCertsDir != "" { err = copySslCerts(startOpts.WorkDir, startOpts.SslCertsDir) diff --git a/test_helpers/pool_helper.go b/test_helpers/pool_helper.go index 0061870de..8c3a7e6ff 100644 --- a/test_helpers/pool_helper.go +++ b/test_helpers/pool_helper.go @@ -219,7 +219,8 @@ func SetClusterRO(servers []string, connOpts tarantool.Opts, roles []bool) error } func StartTarantoolInstances(servers []string, workDirs []string, opts StartOpts) ([]TarantoolInstance, error) { - if len(servers) != len(workDirs) { + isUserWorkDirs := (workDirs != nil) + if isUserWorkDirs && (len(servers) != len(workDirs)) { return nil, fmt.Errorf("number of servers should be equal to number of workDirs") } @@ -227,7 +228,11 @@ func StartTarantoolInstances(servers []string, workDirs []string, opts StartOpts for i, server := range servers { opts.Listen = server - opts.WorkDir = workDirs[i] + if isUserWorkDirs { + opts.WorkDir = workDirs[i] + } else { + opts.WorkDir = "" + } instance, err := StartTarantool(opts) if err != nil { diff --git a/test_helpers/utils.go b/test_helpers/utils.go index 8cf8e2b4b..4862d90d8 100644 --- a/test_helpers/utils.go +++ b/test_helpers/utils.go @@ -1,6 +1,7 @@ package test_helpers import ( + "fmt" "testing" "time" @@ -75,7 +76,8 @@ func SkipIfSQLUnsupported(t testing.TB) { } } -func skipIfLess(t *testing.T, feature string, major, minor, patch uint64) { +// SkipIfLess skips test run if Tarantool version is less than expected. +func SkipIfLess(t *testing.T, reason string, major, minor, patch uint64) { t.Helper() isLess, err := IsTarantoolVersionLess(major, minor, patch) @@ -84,11 +86,13 @@ func skipIfLess(t *testing.T, feature string, major, minor, patch uint64) { } if isLess { - t.Skipf("Skipping test for Tarantool without %s support", feature) + t.Skipf("Skipping test for Tarantool %s", reason) } } -func skipIfGreaterOrEqual(t *testing.T, feature string, major, minor, patch uint64) { +// SkipIfGreaterOrEqual skips test run if Tarantool version is greater or equal +// than expected. +func SkipIfGreaterOrEqual(t *testing.T, reason string, major, minor, patch uint64) { t.Helper() isLess, err := IsTarantoolVersionLess(major, minor, patch) @@ -97,16 +101,40 @@ func skipIfGreaterOrEqual(t *testing.T, feature string, major, minor, patch uint } if !isLess { - t.Skipf("Skipping test for Tarantool with %s support", feature) + t.Skipf("Skipping test for Tarantool %s", reason) } } +// SkipIfFeatureUnsupported skips test run if Tarantool does not yet support a feature. +func SkipIfFeatureUnsupported(t *testing.T, feature string, major, minor, patch uint64) { + t.Helper() + + SkipIfLess(t, fmt.Sprintf("without %s support", feature), major, minor, patch) +} + +// SkipIfFeatureSupported skips test run if Tarantool supports a feature. +// Helper if useful when we want to test if everything is alright +// on older versions. +func SkipIfFeatureSupported(t *testing.T, feature string, major, minor, patch uint64) { + t.Helper() + + SkipIfGreaterOrEqual(t, fmt.Sprintf("with %s support", feature), major, minor, patch) +} + +// SkipIfFeatureDropped skips test run if Tarantool had dropped +// support of a feature. +func SkipIfFeatureDropped(t *testing.T, feature string, major, minor, patch uint64) { + t.Helper() + + SkipIfGreaterOrEqual(t, fmt.Sprintf("with %s support dropped", feature), major, minor, patch) +} + // SkipOfStreamsUnsupported skips test run if Tarantool without streams // support is used. func SkipIfStreamsUnsupported(t *testing.T) { t.Helper() - skipIfLess(t, "streams", 2, 10, 0) + SkipIfFeatureUnsupported(t, "streams", 2, 10, 0) } // SkipOfStreamsUnsupported skips test run if Tarantool without watchers @@ -114,7 +142,7 @@ func SkipIfStreamsUnsupported(t *testing.T) { func SkipIfWatchersUnsupported(t *testing.T) { t.Helper() - skipIfLess(t, "watchers", 2, 10, 0) + SkipIfFeatureUnsupported(t, "watchers", 2, 10, 0) } // SkipIfWatchersSupported skips test run if Tarantool with watchers @@ -122,7 +150,7 @@ func SkipIfWatchersUnsupported(t *testing.T) { func SkipIfWatchersSupported(t *testing.T) { t.Helper() - skipIfGreaterOrEqual(t, "watchers", 2, 10, 0) + SkipIfFeatureSupported(t, "watchers", 2, 10, 0) } // SkipIfIdUnsupported skips test run if Tarantool without @@ -130,7 +158,7 @@ func SkipIfWatchersSupported(t *testing.T) { func SkipIfIdUnsupported(t *testing.T) { t.Helper() - skipIfLess(t, "id requests", 2, 10, 0) + SkipIfFeatureUnsupported(t, "id requests", 2, 10, 0) } // SkipIfIdSupported skips test run if Tarantool with @@ -139,7 +167,23 @@ func SkipIfIdUnsupported(t *testing.T) { func SkipIfIdSupported(t *testing.T) { t.Helper() - skipIfGreaterOrEqual(t, "id requests", 2, 10, 0) + SkipIfFeatureSupported(t, "id requests", 2, 10, 0) +} + +// SkipIfErrorExtendedInfoUnsupported skips test run if Tarantool without +// IPROTO_ERROR (0x52) support is used. +func SkipIfErrorExtendedInfoUnsupported(t *testing.T) { + t.Helper() + + SkipIfFeatureUnsupported(t, "error extended info", 2, 4, 1) +} + +// SkipIfErrorMessagePackTypeUnsupported skips test run if Tarantool without +// MP_ERROR type over iproto support is used. +func SkipIfErrorMessagePackTypeUnsupported(t *testing.T) { + t.Helper() + + SkipIfFeatureUnsupported(t, "error type in MessagePack", 2, 10, 0) } // CheckEqualBoxErrors checks equivalence of tarantool.BoxError objects. @@ -180,36 +224,3 @@ func CheckEqualBoxErrors(t *testing.T, expected tarantool.BoxError, actual taran } } } - -// SkipIfErrorExtendedInfoUnsupported skips test run if Tarantool without -// IPROTO_ERROR (0x52) support is used. -func SkipIfErrorExtendedInfoUnsupported(t *testing.T) { - t.Helper() - - // Tarantool provides extended error info only since 2.4.1 version. - isLess, err := IsTarantoolVersionLess(2, 4, 1) - if err != nil { - t.Fatalf("Could not check the Tarantool version") - } - - if isLess { - t.Skip("Skipping test for Tarantool without error extended info support") - } -} - -// SkipIfErrorExtendedInfoUnsupported skips test run if Tarantool without -// MP_ERROR type over iproto support is used. -func SkipIfErrorMessagePackTypeUnsupported(t *testing.T) { - t.Helper() - - // Tarantool error type over MessagePack supported only since 2.10.0 version. - isLess, err := IsTarantoolVersionLess(2, 10, 0) - if err != nil { - t.Fatalf("Could not check the Tarantool version") - } - - if isLess { - t.Skip("Skipping test for Tarantool without support of error type over MessagePack") - t.Skip("Skipping test for Tarantool without error extended info support") - } -} diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index 6224a938b..e04ab5ab9 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -155,7 +155,6 @@ func runTestMain(m *testing.M) int { inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ InitScript: "config.lua", Listen: server, - WorkDir: "work_dir", User: opts.User, Pass: opts.Pass, WaitStart: 100 * time.Millisecond,