diff --git a/crud/common/sharding_key.lua b/crud/common/sharding_key.lua index eb70351dc..8349e3fae 100644 --- a/crud/common/sharding_key.lua +++ b/crud/common/sharding_key.lua @@ -7,6 +7,7 @@ local dev_checks = require('crud.common.dev_checks') local cache = require('crud.common.sharding_key_cache') local utils = require('crud.common.utils') +local ShardingKeyError = errors.new_class("ShardingKeyError", {capture_stack = false}) local FetchShardingKeyError = errors.new_class('FetchShardingKeyError', {capture_stack = false}) local WrongShardingConfigurationError = errors.new_class('WrongShardingConfigurationError', {capture_stack = false}) @@ -134,12 +135,83 @@ function sharding_key_module.fetch_on_router(space_name) return cache.sharding_key_as_index_obj_map[space_name] end +-- Make sure sharding key definition is a part of primary key. +local function is_part_of_pk(space_name, primary_index_parts, sharding_key_as_index_obj) + dev_checks('string', 'table', 'table') + + if cache.is_part_of_pk[space_name] ~= nil then + return cache.is_part_of_pk[space_name] + end + + local is_part_of_pk = true + local pk_fieldno_map = utils.get_index_fieldno_map(primary_index_parts) + for _, part in ipairs(sharding_key_as_index_obj.parts) do + if pk_fieldno_map[part.fieldno] == nil then + is_part_of_pk = false + break + end + end + cache.is_part_of_pk[space_name] = is_part_of_pk + + return is_part_of_pk +end + +-- Build an array with sharding key values. Function extracts those values from +-- primary key that are part of sharding key (passed as index object). +local function extract_from_index(primary_key, primary_index_parts, sharding_key_as_index_obj) + dev_checks('table', 'table', 'table') + + local primary_index_fieldno_map = utils.get_index_fieldno_map(primary_index_parts) + + local sharding_key = {} + for _, part in ipairs(sharding_key_as_index_obj.parts) do + -- part_number cannot be nil because earlier we checked that tuple + -- field names defined in sharding key definition are part of primary + -- key. + local part_number = primary_index_fieldno_map[part.fieldno] + assert(part_number ~= nil) + local field_value = primary_key[part_number] + table.insert(sharding_key, field_value) + end + + return sharding_key +end + +-- Extract sharding key from pk. +-- Returns a table with sharding key or pair of nil and error. +function sharding_key_module.extract_from_pk(space_name, primary_index_parts, primary_key) + dev_checks('string', 'table', '?') + + local sharding_key_as_index_obj, err = sharding_key_module.fetch_on_router(space_name) + if err ~= nil then + return nil, err + end + if sharding_key_as_index_obj == nil then + return primary_key + end + + local res = is_part_of_pk(space_name, primary_index_parts, sharding_key_as_index_obj) + if res == false then + return nil, ShardingKeyError:new( + "Sharding key for space %q is missed in primary index, specify bucket_id", + space_name + ) + end + if type(primary_key) ~= 'table' then + primary_key = {primary_key} + end + + return extract_from_index(primary_key, primary_index_parts, sharding_key_as_index_obj) +end + function sharding_key_module.init() _G._crud.fetch_on_storage = sharding_key_module.fetch_on_storage end sharding_key_module.internal = { as_index_object = as_index_object, + extract_from_index = extract_from_index, + is_part_of_pk = is_part_of_pk, } return sharding_key_module diff --git a/crud/common/sharding_key_cache.lua b/crud/common/sharding_key_cache.lua index b97b3a9e5..9c3d1929d 100644 --- a/crud/common/sharding_key_cache.lua +++ b/crud/common/sharding_key_cache.lua @@ -1,5 +1,11 @@ local sharding_key_cache = {} sharding_key_cache.sharding_key_as_index_obj_map = nil +sharding_key_cache.is_part_of_pk = {} + +function sharding_key_cache.drop_caches() + sharding_key_cache.sharding_key_as_index_obj_map = nil + sharding_key_cache.is_part_of_pk = {} +end return sharding_key_cache diff --git a/crud/common/utils.lua b/crud/common/utils.lua index e23d48331..d7a629417 100644 --- a/crud/common/utils.lua +++ b/crud/common/utils.lua @@ -421,6 +421,20 @@ function utils.get_bucket_id_fieldno(space, shard_index_name) return bucket_id_index.parts[1].fieldno end +-- Build a map with field number as a keys and part number +-- as a values using index parts as a source. +function utils.get_index_fieldno_map(index_parts) + dev_checks('table') + + local fieldno_map = {} + for i, part in ipairs(index_parts) do + local fieldno = part.fieldno + fieldno_map[fieldno] = i + end + + return fieldno_map +end + -- Build a map with field names as a keys and fieldno's -- as a values using space format as a source. function utils.get_format_fieldno_map(space_format) diff --git a/crud/delete.lua b/crud/delete.lua index 9f0497fe9..9d4b64e87 100644 --- a/crud/delete.lua +++ b/crud/delete.lua @@ -5,6 +5,7 @@ local vshard = require('vshard') local call = require('crud.common.call') local utils = require('crud.common.utils') local sharding = require('crud.common.sharding') +local sharding_key_module = require('crud.common.sharding_key') local dev_checks = require('crud.common.dev_checks') local schema = require('crud.common.schema') @@ -55,7 +56,17 @@ local function call_delete_on_router(space_name, key, opts) key = key:totable() end - local bucket_id = sharding.key_get_bucket_id(key, opts.bucket_id) + local sharding_key = key + if opts.bucket_id == nil then + local err + local primary_index_parts = space.index[0].parts + sharding_key, err = sharding_key_module.extract_from_pk(space_name, primary_index_parts, key) + if err ~= nil then + return nil, err + end + end + + local bucket_id = sharding.key_get_bucket_id(sharding_key, opts.bucket_id) local call_opts = { mode = 'write', timeout = opts.timeout, diff --git a/crud/get.lua b/crud/get.lua index f00957e54..c9ec8cc51 100644 --- a/crud/get.lua +++ b/crud/get.lua @@ -5,6 +5,7 @@ local vshard = require('vshard') local call = require('crud.common.call') local utils = require('crud.common.utils') local sharding = require('crud.common.sharding') +local sharding_key_module = require('crud.common.sharding_key') local dev_checks = require('crud.common.dev_checks') local schema = require('crud.common.schema') @@ -58,7 +59,17 @@ local function call_get_on_router(space_name, key, opts) key = key:totable() end - local bucket_id = sharding.key_get_bucket_id(key, opts.bucket_id) + local sharding_key = key + if opts.bucket_id == nil then + local err + local primary_index_parts = space.index[0].parts + sharding_key, err = sharding_key_module.extract_from_pk(space_name, primary_index_parts, key) + if err ~= nil then + return nil, err + end + end + + local bucket_id = sharding.key_get_bucket_id(sharding_key, opts.bucket_id) local call_opts = { mode = opts.mode or 'read', prefer_replica = opts.prefer_replica, diff --git a/crud/update.lua b/crud/update.lua index 3c5513108..587068ca9 100644 --- a/crud/update.lua +++ b/crud/update.lua @@ -5,6 +5,7 @@ local vshard = require('vshard') local call = require('crud.common.call') local utils = require('crud.common.utils') local sharding = require('crud.common.sharding') +local sharding_key_module = require('crud.common.sharding_key') local dev_checks = require('crud.common.dev_checks') local schema = require('crud.common.schema') @@ -83,6 +84,16 @@ local function call_update_on_router(space_name, key, user_operations, opts) key = key:totable() end + local sharding_key = key + if opts.bucket_id == nil then + local err + local primary_index_parts = space.index[0].parts + sharding_key, err = sharding_key_module.extract_from_pk(space_name, primary_index_parts, key) + if err ~= nil then + return nil, err + end + end + local operations = user_operations if not utils.tarantool_supports_fieldpaths() then operations, err = utils.convert_operations(user_operations, space_format) @@ -91,7 +102,7 @@ local function call_update_on_router(space_name, key, user_operations, opts) end end - local bucket_id = sharding.key_get_bucket_id(key, opts.bucket_id) + local bucket_id = sharding.key_get_bucket_id(sharding_key, opts.bucket_id) local call_opts = { mode = 'write', timeout = opts.timeout, diff --git a/test/entrypoint/srv_ddl.lua b/test/entrypoint/srv_ddl.lua index 57fa3ef7d..3b3bb6b06 100755 --- a/test/entrypoint/srv_ddl.lua +++ b/test/entrypoint/srv_ddl.lua @@ -95,11 +95,17 @@ package.preload['customers-storage'] = function() table.insert(customers_secondary_idx_name_key_schema.indexes, secondary_index) table.insert(customers_secondary_idx_name_key_schema.indexes, bucket_id_index) + local customers_age_key_schema = table.deepcopy(customers_schema) + customers_age_key_schema.sharding_key = {'age'} + table.insert(customers_age_key_schema.indexes, primary_index) + table.insert(customers_age_key_schema.indexes, bucket_id_index) + local schema = { spaces = { customers_name_key = customers_name_key_schema, customers_name_key_uniq_index = customers_name_key_uniq_index_schema, customers_name_key_non_uniq_index = customers_name_key_non_uniq_index_schema, + customers_age_key = customers_age_key_schema, customers_secondary_idx_name_key = customers_secondary_idx_name_key_schema, } } @@ -110,6 +116,11 @@ package.preload['customers-storage'] = function() error(err) end end + + rawset(_G, 'set_sharding_key', function(space_name, sharding_key_def) + local fieldno_sharding_key = 2 + box.space['_ddl_sharding_key']:update(space_name, {{'=', fieldno_sharding_key, sharding_key_def}}) + end) end, } end diff --git a/test/integration/ddl_sharding_key_test.lua b/test/integration/ddl_sharding_key_test.lua index a8264affb..06ff85f9d 100644 --- a/test/integration/ddl_sharding_key_test.lua +++ b/test/integration/ddl_sharding_key_test.lua @@ -1,5 +1,5 @@ local fio = require('fio') - +local crud = require('crud') local t = require('luatest') local helpers = require('test.helper') @@ -42,8 +42,246 @@ pgroup.before_each(function(g) helpers.truncate_space_on_cluster(g.cluster, 'customers_name_key_uniq_index') helpers.truncate_space_on_cluster(g.cluster, 'customers_name_key_non_uniq_index') helpers.truncate_space_on_cluster(g.cluster, 'customers_secondary_idx_name_key') + helpers.truncate_space_on_cluster(g.cluster, 'customers_age_key') end) +local function check_get(g, space_name, id, name) + local result, err = g.cluster.main_server.net_box:call('crud.get', { + space_name, {id, name} + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) +end + +pgroup.test_insert_object_get = function(g) + -- insert_object + local result, err = g.cluster.main_server.net_box:call( + 'crud.insert_object', {'customers_name_key', {id = 1, name = 'Fedor', age = 59}}) + + t.assert_equals(err, nil) + t.assert_equals(result.metadata, { + {is_nullable = false, name = 'id', type = 'unsigned'}, + {is_nullable = false, name = 'bucket_id', type = 'unsigned'}, + {is_nullable = false, name = 'name', type = 'string'}, + {is_nullable = false, name = 'age', type = 'number'}, + }) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{id = 1, bucket_id = 86, name = 'Fedor', age = 59}}) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {1, 'Fedor'}}) + + t.assert_equals(err, nil) + t.assert(result ~= nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{age = 59, bucket_id = 86, id = 1, name = "Fedor"}}) + + -- insert_object again + local obj, err = g.cluster.main_server.net_box:call( + 'crud.insert_object', {'customers_name_key', {id = 1, name = 'Alexander', age = 37}}) + + t.assert_equals(err, nil) + t.assert_equals(obj.rows, {{1, 1690, "Alexander", 37}}) +end + +pgroup.test_insert_get = function(g) + -- insert + local result, err = g.cluster.main_server.net_box:call( + 'crud.insert', {'customers_name_key', {2, box.NULL, 'Ivan', 20}}) + + t.assert_equals(err, nil) + t.assert_equals(result.metadata, { + {is_nullable = false, name = 'id', type = 'unsigned'}, + {is_nullable = false, name = 'bucket_id', type = 'unsigned'}, + {is_nullable = false, name = 'name', type = 'string'}, + {is_nullable = false, name = 'age', type = 'number'}, + }) + t.assert_equals(result.rows, {{2, 1366, "Ivan", 20}}) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {2, 'Ivan'}}) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(result.rows, {{2, 1366, "Ivan", 20}}) +end + +pgroup.test_update = function(g) + local tuple = {2, box.NULL, 'Ivan', 20} + + local update_operations = { + {'+', 'age', 10}, + } + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local result, err = g.cluster.main_server.net_box:call('crud.update', { + 'customers_name_key', {2, 'Ivan'}, update_operations, + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {2, 'Ivan'}}) + + t.assert_equals(err, nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{age = 30, bucket_id = 1366, id = 2, name = "Ivan"}}) +end + +pgroup.test_delete = function(g) + local tuple = {2, box.NULL, 'Ivan', 20} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local result, err = g.cluster.main_server.net_box:call('crud.delete', { + 'customers_name_key', {2, 'Ivan'}, + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + + local result, err = g.cluster.main_server.net_box:call('crud.get', { + 'customers_name_key', {2, 'Ivan'} + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 0) +end + +pgroup.test_insert = function(g) + local tuple = {2, box.NULL, 'Ivan', 20} + + -- insert + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_name_key', tuple, + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(result.rows[1], {2, 1366, "Ivan", 20}) + + check_get(g, 'customers_name_key', 2, 'Ivan') +end + +pgroup.test_replace = function(g) + local tuple = {2, box.NULL, 'Jane', 21} + + -- replace + local result, err = g.cluster.main_server.net_box:call('crud.replace', { + 'customers_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(result.rows[1], {2, 1538, "Jane", 21}) + + check_get(g, 'customers_name_key', 2, 'Jane') +end + +pgroup.test_replace_object = function(g) + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {44, 'Ivan'}}) + + t.assert_equals(err, nil) + t.assert_equals(result.metadata, { + {is_nullable = false, name = 'id', type = 'unsigned'}, + {is_nullable = false, name = 'bucket_id', type = 'unsigned'}, + {is_nullable = false, name = 'name', type = 'string'}, + {is_nullable = false, name = 'age', type = 'number'}, + }) + t.assert_equals(#result.rows, 0) + + -- replace_object + local result, err = g.cluster.main_server.net_box:call( + 'crud.replace_object', {'customers_name_key', {id = 44, name = 'John Doe', age = 25}}) + + t.assert_equals(err, nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{id = 44, bucket_id = 1035, name = 'John Doe', age = 25}}) + + -- replace_object + local result, err = g.cluster.main_server.net_box:call( + 'crud.replace_object', {'customers_name_key', {id = 44, name = 'Jane Doe', age = 18}}) + + t.assert_equals(err, nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{id = 44, bucket_id = 2194, name = 'Jane Doe', age = 18}}) +end + +pgroup.test_upsert_object = function(g) + -- upsert_object first time + local result, err = g.cluster.main_server.net_box:call( + 'crud.upsert_object', {'customers_name_key', {id = 66, name = 'Jack Sparrow', age = 25}, { + {'+', 'age', 25}, + }}) + + t.assert_equals(#result.rows, 0) + t.assert_equals(result.metadata, { + {is_nullable = false, name = 'id', type = 'unsigned'}, + {is_nullable = false, name = 'bucket_id', type = 'unsigned'}, + {is_nullable = false, name = 'name', type = 'string'}, + {is_nullable = false, name = 'age', type = 'number'}, + }) + t.assert_equals(err, nil) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {66, 'Jack Sparrow'}}) + + t.assert_equals(err, nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{id = 66, bucket_id = 2719, name = 'Jack Sparrow', age = 25}}) + + -- upsert_object the same query second time when tuple exists + local result, err = g.cluster.main_server.net_box:call( + 'crud.upsert_object', {'customers_name_key', {id = 66, name = 'Jack Sparrow', age = 25}, { + {'+', 'age', 25}, + }}) + + t.assert_equals(#result.rows, 0) + t.assert_equals(err, nil) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_name_key', {66, 'Jack Sparrow'}}) + + t.assert_equals(err, nil) + local objects = crud.unflatten_rows(result.rows, result.metadata) + t.assert_equals(objects, {{age = 50, bucket_id = 2719, id = 66, name = "Jack Sparrow"}}) +end + +pgroup.test_upsert = function(g) + local tuple = {1, box.NULL, 'John', 25} + + -- upsert + local result, err = g.cluster.main_server.net_box:call('crud.upsert', { + 'customers_name_key', tuple, {} + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 0) + + check_get(g, 'customers_name_key', 1, 'John') +end + pgroup.test_select = function(g) local tuple = {2, box.NULL, 'Ivan', 20} @@ -67,6 +305,73 @@ pgroup.test_select = function(g) t.assert_equals(#result.rows, 1) end +pgroup.test_incomplete_sharding_key_delete = function(g) + local tuple = {2, box.NULL, 'Viktor Pelevin', 58} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_age_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local result, err = g.cluster.main_server.net_box:call('crud.delete', { + 'customers_age_key', {58, 'Viktor Pelevin'} + }) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_age_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end + +pgroup.test_incomplete_sharding_key_get = function(g) + local tuple = {2, box.NULL, 'Viktor Pelevin', 58} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_age_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local result, err = g.cluster.main_server.net_box:call('crud.get', { + 'customers_age_key', {58, 'Viktor Pelevin'} + }) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_age_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end + +pgroup.test_incomplete_sharding_key_update = function(g) + local tuple = {2, box.NULL, 'Viktor Pelevin', 58} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_age_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local update_operations = { + {'=', 'age', 60}, + } + + local result, err = g.cluster.main_server.net_box:call('crud.update', { + 'customers_age_key', {2, 'Viktor Pelevin'}, update_operations, + }) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_age_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end + -- Right now CRUD's plan for select doesn't support sharding key and it leads -- to map reduce (select on all replicasets). To avoid map-reduce one need to -- add a separate index by field name, used in select's condition. We plan to @@ -179,3 +484,69 @@ pgroup.test_non_unique_index = function(g) t.assert_not_equals(result, nil) t.assert_equals(#result.rows, 2) end + +pgroup.test_get_secondary_idx = function(g) + local tuple = {4, box.NULL, 'Leo', 44} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_secondary_idx_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + -- get + local result, err = g.cluster.main_server.net_box:call('crud.get', {'customers_secondary_idx_name_key', {'Leo'}}) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_secondary_idx_name_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end + +pgroup.test_update_secondary_idx = function(g) + local tuple = {6, box.NULL, 'Victor', 58} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_secondary_idx_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local update_operations = { + {'=', 'age', 58}, + } + + local result, err = g.cluster.main_server.net_box:call('crud.update', { + 'customers_secondary_idx_name_key', {'Victor'}, update_operations, + }) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_secondary_idx_name_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end + +pgroup.test_delete_secondary_idx = function(g) + local tuple = {8, box.NULL, 'Alexander', 37} + + -- insert tuple + local result, err = g.cluster.main_server.net_box:call('crud.insert', { + 'customers_secondary_idx_name_key', tuple + }) + + t.assert_equals(err, nil) + t.assert_not_equals(result, nil) + t.assert_equals(#result.rows, 1) + + local result, err = g.cluster.main_server.net_box:call('crud.delete', { + 'customers_secondary_idx_name_key', {'Alexander'} + }) + + t.assert_str_contains(err.err, + "Sharding key for space \"customers_secondary_idx_name_key\" is missed in primary index, specify bucket_id") + t.assert_equals(result, nil) +end diff --git a/test/unit/sharding_key_test.lua b/test/unit/sharding_key_test.lua index fdd789073..110eab1c7 100644 --- a/test/unit/sharding_key_test.lua +++ b/test/unit/sharding_key_test.lua @@ -1,5 +1,6 @@ local t = require('luatest') local sharding_key_module = require('crud.common.sharding_key') +local cache = require('crud.common.sharding_key_cache') local utils = require('crud.common.utils') local helpers = require('test.helper') @@ -18,7 +19,6 @@ g.before_each(function() end box.schema.space.create('_ddl_sharding_key', { format = sharding_key_format, - if_not_exists = true }) box.space._ddl_sharding_key:create_index('pk') box.schema.space.create('fetch_on_storage') @@ -30,6 +30,7 @@ g.after_each(function() box.space._ddl_sharding_key:drop() end box.space.fetch_on_storage:drop() + cache.drop_caches() end) g.test_as_index_object_positive = function() @@ -103,3 +104,130 @@ g.test_fetch_on_storage_negative = function() local metadata_map = sharding_key_module.fetch_on_storage() t.assert_equals(metadata_map, nil) end + +g.test_extract_from_index_sharding_key_direct_order = function() + local primary_index_parts = { + {fieldno = 1}, + {fieldno = 2}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 1}, + {fieldno = 2}, + } + } + local primary_key = {'name', 'age'} + + local extract_from_index = sharding_key_module.internal.extract_from_index + local sharding_key = extract_from_index(primary_key, + primary_index_parts, + sharding_key_as_index_obj) + t.assert_equals(sharding_key, {'name', 'age'}) +end + +g.test_extract_from_index_sharding_key_reverse_order = function() + local primary_index_parts = { + {fieldno = 1}, + {fieldno = 2}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 2}, + {fieldno = 1}, + } + } + local primary_key = {'name', 'age'} + + local extract_from_index = sharding_key_module.internal.extract_from_index + local sharding_key = extract_from_index(primary_key, + primary_index_parts, + sharding_key_as_index_obj) + t.assert_equals(sharding_key, {'age', 'name'}) +end + +g.test_extract_from_index_sharding_key_single_field = function() + local primary_index_parts = { + {fieldno = 1}, + {fieldno = 2}, + {fieldno = 3}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 2}, + } + } + local primary_key = {'name', 'age', 'location'} + + local extract_from_index = sharding_key_module.internal.extract_from_index + local sharding_key = extract_from_index(primary_key, + primary_index_parts, + sharding_key_as_index_obj) + t.assert_equals(sharding_key, {'age'}) +end + +g.test_extract_from_index_sharding_key_none_fields = function() + local primary_index_parts = { + {fieldno = 1}, + {fieldno = 3}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 2}, + } + } + local primary_key = {'name', 'age', 'location'} + + local extract_from_index = sharding_key_module.internal.extract_from_index + local ok, err = pcall(extract_from_index, primary_key, + primary_index_parts, + sharding_key_as_index_obj) + t.assert_equals(ok, false) + t.assert_str_contains(err, 'assertion failed') +end + +g.test_get_index_fieldno_map = function() + local index_parts = { + {fieldno = 2}, + {fieldno = 3}, + } + + local fieldno_map = utils.get_index_fieldno_map(index_parts) + t.assert_equals(fieldno_map, { + [2] = 1, + [3] = 2 + }) +end + +g.test_is_part_of_pk_positive = function() + local space_name = 'is_part_of_pk' + local index_parts = { + {fieldno = 2}, + {fieldno = 3}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 2}, + } + } + + local is_part_of_pk = sharding_key_module.internal.is_part_of_pk + local res = is_part_of_pk(space_name, index_parts, sharding_key_as_index_obj) + t.assert_equals(res, true) +end + +g.test_is_part_of_pk_negative = function() + local space_name = 'is_part_of_pk' + local index_parts = { + {fieldno = 1}, + {fieldno = 3}, + } + local sharding_key_as_index_obj = { + parts = { + {fieldno = 2}, + } + } + + local is_part_of_pk = sharding_key_module.internal.is_part_of_pk + local res = is_part_of_pk(space_name, index_parts, sharding_key_as_index_obj) + t.assert_equals(res, false) +end