[tarantool-patches] Re: [PATCH v4 4/4] box: introduce functional indexes
Kirill Shcherbatov
kshcherbatov at tarantool.org
Thu Jul 25 14:20:06 MSK 2019
On 24.07.2019 23:22, Konstantin Osipov wrote:
> * Kirill Shcherbatov <kshcherbatov at tarantool.org> [19/07/24 10:38]:
>> lua_code = [[function(tuple)
>> local address = string.split(tuple[2])
>> local ret = {}
>> for _, v in pairs(address) do
>> table.insert(ret, {utf8.upper(v)})
>> end
>> return unpack(ret)
>> end]]
>
> I think the convention should be that a multikey function must always
> return a table (a msgpack array for a C function).
>
> It's OK to wrap every key into a table as long as it simplifies
> the code (key part count is encoded automatically by struct port), but otherwise
> I would stick to scalars.
Ok, now a user must implicitly specify is_multikey and return a table of keys;
The corresponding change diff is below:
======================================================
---
src/box/index_def.h | 4 +++
src/box/key_def.h | 2 +-
src/box/key_list.h | 2 +-
src/box/index_def.c | 2 ++
src/box/key_def.c | 5 +--
src/box/key_list.c | 26 ++++++++++++--
src/box/lua/key_def.c | 2 +-
src/box/memtx_engine.c | 2 ++
src/box/memtx_tree.c | 3 ++
src/box/sql.c | 3 +-
src/box/sql/build.c | 2 +-
src/box/sql/select.c | 3 +-
src/box/sql/where.c | 2 +-
src/box/vinyl.c | 4 ++-
test/unit/luaT_tuple_new.c | 2 +-
test/unit/merger.test.c | 6 ++--
src/box/alter.cc | 3 +-
src/box/lua/space.cc | 18 +++++++++-
src/box/schema.cc | 3 +-
test/engine/functional.result | 62 ++++++++++++++++++++++++++-------
test/engine/functional.test.lua | 38 +++++++++++++-------
21 files changed, 151 insertions(+), 43 deletions(-)
diff --git a/src/box/index_def.h b/src/box/index_def.h
index 71b6db32d..f7788c63e 100644
--- a/src/box/index_def.h
+++ b/src/box/index_def.h
@@ -165,6 +165,8 @@ struct index_opts {
struct index_stat *stat;
/** Identifier of the functional index function. */
uint32_t func_id;
+ /** Whether functional index extractor is multikey. */
+ bool is_multikey;
};
extern const struct index_opts index_opts_default;
@@ -211,6 +213,8 @@ index_opts_cmp(const struct index_opts *o1, const struct index_opts *o2)
return o1->bloom_fpr < o2->bloom_fpr ? -1 : 1;
if (o1->func_id != o2->func_id)
return o1->func_id - o2->func_id;
+ if (o1->is_multikey != o2->is_multikey)
+ return o1->is_multikey - o2->is_multikey;
return 0;
}
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 5c898fc87..4357408b7 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -340,7 +340,7 @@ key_def_sizeof(uint32_t part_count, uint32_t path_pool_size)
*/
struct key_def *
key_def_new(const struct key_part_def *parts, uint32_t part_count,
- bool is_functional);
+ bool is_functional, bool is_multikey);
/**
* Dump part definitions of the given key def.
diff --git a/src/box/key_list.h b/src/box/key_list.h
index 9107dfeeb..f7ed344c8 100644
--- a/src/box/key_list.h
+++ b/src/box/key_list.h
@@ -52,7 +52,7 @@ struct tuple;
* Uses fiber region to allocate memory.
*/
const char *
-key_list_create(struct tuple *tuple, struct func *func,
+key_list_create(struct tuple *tuple, struct func *func, bool is_multikey,
const char **data_end, uint32_t *key_count);
/**
diff --git a/src/box/index_def.c b/src/box/index_def.c
index 7d06c65f3..420ca14f4 100644
--- a/src/box/index_def.c
+++ b/src/box/index_def.c
@@ -51,6 +51,7 @@ const struct index_opts index_opts_default = {
/* .lsn = */ 0,
/* .stat = */ NULL,
/* .func = */ 0,
+ /* .is_multikey = */ false,
};
const struct opt_def index_opts_reg[] = {
@@ -65,6 +66,7 @@ const struct opt_def index_opts_reg[] = {
OPT_DEF("bloom_fpr", OPT_FLOAT, struct index_opts, bloom_fpr),
OPT_DEF("lsn", OPT_INT64, struct index_opts, lsn),
OPT_DEF("func", OPT_UINT32, struct index_opts, func_id),
+ OPT_DEF("is_multikey", OPT_BOOL, struct index_opts, is_multikey),
OPT_DEF_LEGACY("sql"),
OPT_END,
};
diff --git a/src/box/key_def.c b/src/box/key_def.c
index ffc34db59..ea46a1583 100644
--- a/src/box/key_def.c
+++ b/src/box/key_def.c
@@ -242,7 +242,7 @@ key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno,
struct key_def *
key_def_new(const struct key_part_def *parts, uint32_t part_count,
- bool is_functional)
+ bool is_functional, bool is_multikey)
{
size_t sz = 0;
for (uint32_t i = 0; i < part_count; i++)
@@ -279,6 +279,7 @@ key_def_new(const struct key_part_def *parts, uint32_t part_count,
goto error;
}
if (is_functional) {
+ def->is_multikey = is_multikey;
def->part_count_for_func_index = part_count;
if (!key_def_is_sequential(def) || parts->fieldno != 0 ||
def->has_json_paths) {
@@ -842,7 +843,7 @@ key_def_find_pk_in_cmp_def(const struct key_def *cmp_def,
}
/* Finally, allocate the new key definition. */
- extracted_def = key_def_new(parts, pk_def->part_count, false);
+ extracted_def = key_def_new(parts, pk_def->part_count, false, false);
out:
region_truncate(region, region_svp);
return extracted_def;
diff --git a/src/box/key_list.c b/src/box/key_list.c
index 5b5552116..6b800d9c7 100644
--- a/src/box/key_list.c
+++ b/src/box/key_list.c
@@ -33,6 +33,7 @@
#include "errcode.h"
#include "diag.h"
#include "func.h"
+#include "fiber.h"
#include "key_def.h"
#include "port.h"
#include "tt_static.h"
@@ -40,9 +41,12 @@
#include "tuple_compare.h"
const char *
-key_list_create(struct tuple *tuple, struct func *func,
+key_list_create(struct tuple *tuple, struct func *func, bool is_multikey,
const char **data_end, uint32_t *key_count)
{
+ struct region *region = &fiber()->gc;
+ size_t region_svp = region_used(region);
+
struct port out_port, in_port;
port_tuple_create(&in_port);
port_tuple_add(&in_port, tuple);
@@ -52,15 +56,31 @@ key_list_create(struct tuple *tuple, struct func *func,
goto error;
uint32_t key_data_sz;
const char *key_data = port_get_msgpack(&out_port, &key_data_sz);
+ *data_end = key_data + key_data_sz;
port_destroy(&out_port);
if (key_data == NULL)
goto error;
assert(mp_typeof(*key_data) == MP_ARRAY);
- *data_end = key_data + key_data_sz;
- *key_count = mp_decode_array(&key_data);
+ if (mp_decode_array(&key_data) != 1) {
+ diag_set(ClientError, ER_FUNC_INDEX_FUNC,
+ func->def->name, "to many values were returned");
+ goto error;
+ }
+ if (is_multikey) {
+ if (mp_typeof(*key_data) != MP_ARRAY) {
+ diag_set(ClientError, ER_FUNC_INDEX_FUNC,
+ func->def->name,
+ "multikey function mustn't return scalar");
+ goto error;
+ }
+ *key_count = mp_decode_array(&key_data);
+ } else {
+ *key_count = 1;
+ }
return key_data;
error:
+ region_truncate(region, region_svp);
diag_set(ClientError, ER_FUNC_INDEX_FUNC, func->def->name,
diag_last_error(diag_get())->errmsg);
return NULL;
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 3a3a5ec0c..e572fec1b 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -445,7 +445,7 @@ lbox_key_def_new(struct lua_State *L)
lua_pop(L, 1);
}
- struct key_def *key_def = key_def_new(parts, part_count, false);
+ struct key_def *key_def = key_def_new(parts, part_count, false, false);
region_truncate(region, region_svp);
if (key_def == NULL)
return luaT_error(L);
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index 9f8d5a6a4..8589d2f1e 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -1250,6 +1250,8 @@ memtx_index_def_change_requires_rebuild(struct index *index,
return true;
if (old_def->opts.func_id != new_def->opts.func_id)
return true;
+ if (old_def->opts.is_multikey != new_def->opts.is_multikey)
+ return true;
const struct key_def *old_cmp_def, *new_cmp_def;
if (index_depends_on_pk(index)) {
diff --git a/src/box/memtx_tree.c b/src/box/memtx_tree.c
index f24fec15f..b11f09dd5 100644
--- a/src/box/memtx_tree.c
+++ b/src/box/memtx_tree.c
@@ -863,6 +863,7 @@ memtx_tree_index_replace_functional(struct index *base, struct tuple *old_tuple,
struct key_list_iterator it;
if (new_tuple != NULL) {
key_list = key_list_create(new_tuple, cmp_def->func_index_func,
+ cmp_def->is_multikey,
&key_list_end, &key_cnt);
if (key_list == NULL)
goto end;
@@ -926,6 +927,7 @@ memtx_tree_index_replace_functional(struct index *base, struct tuple *old_tuple,
}
if (old_tuple != NULL) {
key_list = key_list_create(old_tuple, cmp_def->func_index_func,
+ cmp_def->is_multikey,
&key_list_end, &key_cnt);
if (key_list == NULL)
goto end;
@@ -1101,6 +1103,7 @@ memtx_tree_index_build_next_functional(struct index *base, struct tuple *tuple)
const char *key_list;
const char *key_list_end;
key_list = key_list_create(tuple, cmp_def->func_index_func,
+ cmp_def->is_multikey,
&key_list_end, &key_cnt);
if (key_list == NULL)
return -1;
diff --git a/src/box/sql.c b/src/box/sql.c
index 0ab3a506f..ba5bcbf10 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -347,7 +347,8 @@ sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
}
}
struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
- field_count, false);
+ field_count, false,
+ false);
if (ephemer_key_def == NULL)
return NULL;
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0a6759e41..b965c7c5c 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2338,7 +2338,7 @@ index_fill_def(struct Parse *parse, struct index *index,
part->coll_id = coll_id;
part->path = NULL;
}
- key_def = key_def_new(key_parts, expr_list->nExpr, false);
+ key_def = key_def_new(key_parts, expr_list->nExpr, false, false);
if (key_def == NULL)
goto tnt_error;
/*
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index c312f61f1..bb7fa783e 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1438,7 +1438,8 @@ sql_key_info_to_key_def(struct sql_key_info *key_info)
{
if (key_info->key_def == NULL) {
key_info->key_def = key_def_new(key_info->parts,
- key_info->part_count, false);
+ key_info->part_count,
+ false, false);
}
return key_info->key_def;
}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index ed507bf4d..b85243a88 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -2775,7 +2775,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */
part.coll_id = COLL_NONE;
part.path = NULL;
- struct key_def *key_def = key_def_new(&part, 1, false);
+ struct key_def *key_def = key_def_new(&part, 1, false, false);
if (key_def == NULL) {
tnt_error:
pWInfo->pParse->is_aborted = true;
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index c938350d9..dce32b574 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -993,6 +993,8 @@ vinyl_index_def_change_requires_rebuild(struct index *index,
return true;
if (old_def->opts.func_id != new_def->opts.func_id)
return true;
+ if (old_def->opts.is_multikey != new_def->opts.is_multikey)
+ return true;
assert(index_depends_on_pk(index));
const struct key_def *old_cmp_def = old_def->cmp_def;
@@ -3172,7 +3174,7 @@ vy_send_lsm(struct vy_join_ctx *ctx, struct vy_lsm_recovery_info *lsm_info)
/* Create key definition and tuple format. */
ctx->key_def = key_def_new(lsm_info->key_parts,
- lsm_info->key_part_count, false);
+ lsm_info->key_part_count, false, false);
if (ctx->key_def == NULL)
goto out;
ctx->format = vy_stmt_format_new(&ctx->env->stmt_env, &ctx->key_def, 1,
diff --git a/test/unit/luaT_tuple_new.c b/test/unit/luaT_tuple_new.c
index 609d64e45..d5c59c975 100644
--- a/test/unit/luaT_tuple_new.c
+++ b/test/unit/luaT_tuple_new.c
@@ -124,7 +124,7 @@ test_basic(struct lua_State *L)
part.nullable_action = ON_CONFLICT_ACTION_DEFAULT;
part.sort_order = SORT_ORDER_ASC;
part.path = NULL;
- struct key_def *key_def = key_def_new(&part, 1, false);
+ struct key_def *key_def = key_def_new(&part, 1, false, false);
box_tuple_format_t *another_format = box_tuple_format_new(&key_def, 1);
key_def_delete(key_def);
diff --git a/test/unit/merger.test.c b/test/unit/merger.test.c
index 345a2364e..8a4379a10 100644
--- a/test/unit/merger.test.c
+++ b/test/unit/merger.test.c
@@ -214,7 +214,8 @@ test_merger(struct tuple_format *format)
merge_source_array_new(true),
};
- struct key_def *key_def = key_def_new(&key_part_unsigned, 1, false);
+ struct key_def *key_def = key_def_new(&key_part_unsigned, 1,
+ false, false);
struct merge_source *merger = merger_new(key_def, sources, source_count,
false);
key_def_delete(key_def);
@@ -252,7 +253,8 @@ test_basic()
plan(4);
header();
- struct key_def *key_def = key_def_new(&key_part_integer, 1, false);
+ struct key_def *key_def = key_def_new(&key_part_integer, 1,
+ false, false);
struct tuple_format *format = box_tuple_format_new(&key_def, 1);
assert(format != NULL);
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 3363e2b85..0b23dd6bb 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -302,7 +302,8 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *space)
space->def->fields,
space->def->field_count, &fiber()->gc) != 0)
diag_raise();
- key_def = key_def_new(part_def, part_count, opts.func_id > 0);
+ key_def = key_def_new(part_def, part_count, opts.func_id > 0,
+ opts.is_multikey);
if (key_def == NULL)
diag_raise();
struct index_def *index_def =
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 72c6bcada..6a778b2f2 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -42,6 +42,8 @@ extern "C" {
#include <lualib.h>
} /* extern "C" */
+#include "box/func.h"
+#include "box/func_def.h"
#include "box/space.h"
#include "box/schema.h"
#include "box/user_def.h"
@@ -336,8 +338,22 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
}
if (index_opts->func_id > 0) {
+ lua_pushstring(L, "func");
+ lua_newtable(L);
+
lua_pushnumber(L, index_opts->func_id);
- lua_setfield(L, -2, "func_id");
+ lua_setfield(L, -2, "fid");
+
+ lua_pushboolean(L, index_opts->is_multikey);
+ lua_setfield(L, -2, "is_multikey");
+
+ struct func *func = func_by_id(index_opts->func_id);
+ if (func != NULL) {
+ lua_pushstring(L, func->def->name);
+ lua_setfield(L, -2, "name");
+ }
+
+ lua_settable(L, -3);
}
lua_pushstring(L, index_type_strs[index_def->type]);
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 5d4a3ff00..d5ccdd1b6 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -266,7 +266,8 @@ sc_space_new(uint32_t id, const char *name,
uint32_t key_part_count,
struct trigger *replace_trigger)
{
- struct key_def *key_def = key_def_new(key_parts, key_part_count, false);
+ struct key_def *key_def = key_def_new(key_parts, key_part_count,
+ false, false);
if (key_def == NULL)
diag_raise();
auto key_def_guard =
diff --git a/test/engine/functional.result b/test/engine/functional.result
index 8a0eba614..ca8d0e366 100644
--- a/test/engine/functional.result
+++ b/test/engine/functional.result
@@ -159,14 +159,33 @@ idx:drop()
| ---
| ...
--- Invalid functional index extractor routine return: the second returned key invalid.
+-- Invalid functional index extractor routine return: invalid return format for multikey index.
lua_code = [[function(tuple) return {"hello", "world"}, {1, 2} end]]
| ---
| ...
+box.schema.func.create('invalidreturn2', {body = lua_code, is_deterministic = true, is_sandboxed = true})
+ | ---
+ | ...
+idx = s:create_index('idx', {func = box.func.invalidreturn2.id, is_multikey = true, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+ | ---
+ | ...
+s:insert({1})
+ | ---
+ | - error: 'Failed to build a key for functional index ''invalidreturn2'': Failed to
+ | build a key for functional index ''invalidreturn2'': to many values were returned'
+ | ...
+idx:drop()
+ | ---
+ | ...
+
+-- Invalid functional index extractor routine return: the second returned key invalid.
+lua_code = [[function(tuple) return {{"hello", "world"}, {1, 2}} end]]
+ | ---
+ | ...
box.schema.func.create('invalidreturn3', {body = lua_code, is_deterministic = true, is_sandboxed = true})
| ---
| ...
-idx = s:create_index('idx', {func = box.func.invalidreturn3.id, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+idx = s:create_index('idx', {func = box.func.invalidreturn3.id, is_multikey = true, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
| ---
| ...
s:insert({1})
@@ -178,6 +197,25 @@ idx:drop()
| ---
| ...
+-- Invalid functional index extractor routine return: multikey return in case of regular index.
+lua_code = [[function(tuple) return {{"hello", "world"}, {1, 2}} end]]
+ | ---
+ | ...
+box.schema.func.create('invalidreturn4', {body = lua_code, is_deterministic = true, is_sandboxed = true})
+ | ---
+ | ...
+idx = s:create_index('idx', {func = box.func.invalidreturn4.id, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+ | ---
+ | ...
+s:insert({1})
+ | ---
+ | - error: 'Failed to build a key for functional index ''invalidreturn4'': Supplied
+ | key type of part 0 does not match index part type: expected unsigned'
+ | ...
+idx:drop()
+ | ---
+ | ...
+
-- Invalid functional extractor: runtime extractor error
test_run:cmd("setopt delimiter ';'")
| ---
@@ -273,7 +311,7 @@ collectgarbage()
s = box.schema.space.create('withdata', {engine = engine})
| ---
| ...
-lua_code = [[function(tuple) return {tuple[1] + tuple[2]}, {tuple[1] + tuple[2]}, {tuple[1]} end]]
+lua_code = [[function(tuple) return {{tuple[1] + tuple[2]}, {tuple[1] + tuple[2]}, {tuple[1]}} end]]
| ---
| ...
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
@@ -282,7 +320,7 @@ box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_san
pk = s:create_index('pk')
| ---
| ...
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
| ---
| ...
s:insert({1, 2})
@@ -335,7 +373,7 @@ box.schema.func.drop('extr')
s = box.schema.space.create('withdata', {engine = engine})
| ---
| ...
-lua_code = [[function(tuple) return {600 + tuple[1], 600 + tuple[2]}, {500 + tuple[1], 500 + tuple[2]} end]]
+lua_code = [[function(tuple) return {{600 + tuple[1], 600 + tuple[2]}, {500 + tuple[1], 500 + tuple[2]}} end]]
| ---
| ...
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
@@ -344,7 +382,7 @@ box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_san
pk = s:create_index('pk')
| ---
| ...
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}, {2, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}, {2, 'integer'}}})
| ---
| ...
s:insert({1, 2})
@@ -390,7 +428,7 @@ box.schema.func.drop('extr')
s = box.schema.space.create('withdata', {engine = engine})
| ---
| ...
-lua_code = [[function(tuple) return {500 + tuple[1]}, {500 + tuple[2]}, {500 + tuple[2]} end]]
+lua_code = [[function(tuple) return {{500 + tuple[1]}, {500 + tuple[2]}, {500 + tuple[2]}} end]]
| ---
| ...
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
@@ -399,7 +437,7 @@ box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_san
pk = s:create_index('pk')
| ---
| ...
-idx = s:create_index('idx', {unique = false, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = false, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
| ---
| ...
s:insert({1, 2})
@@ -521,7 +559,7 @@ lua_code = [[function(tuple)
local address = string.split(tuple[2])
local ret = {}
for _, v in pairs(address) do table.insert(ret, {utf8.upper(v)}) end
- return unpack(ret)
+ return ret
end]]
test_run:cmd("setopt delimiter ''");
| ---
@@ -529,7 +567,7 @@ test_run:cmd("setopt delimiter ''");
box.schema.func.create('addr_extractor', {body = lua_code, is_deterministic = true, is_sandboxed = true})
| ---
| ...
-idx = s:create_index('addr', {unique = false, func = box.func.addr_extractor.id, parts = {{1, 'string', collation = 'unicode_ci'}}})
+idx = s:create_index('addr', {unique = false, func = box.func.addr_extractor.id, is_multikey = true, parts = {{1, 'string', collation = 'unicode_ci'}}})
| ---
| ...
idx:select('uk')
@@ -559,13 +597,13 @@ s = box.schema.space.create('withdata', {engine = engine})
pk = s:create_index('pk')
| ---
| ...
-lua_code = [[function(tuple) if tuple[1] % 2 == 1 then return {tuple[1]} end end]]
+lua_code = [[function(tuple) if tuple[1] % 2 == 1 then return {{tuple[1]}} else return {} end end]]
| ---
| ...
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
| ---
| ...
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
| ---
| ...
s:insert({1})
diff --git a/test/engine/functional.test.lua b/test/engine/functional.test.lua
index 92a74aab7..ae88f2900 100644
--- a/test/engine/functional.test.lua
+++ b/test/engine/functional.test.lua
@@ -60,10 +60,24 @@ idx = s:create_index('idx', {func = box.func.invalidreturn1.id, parts = {{1, 'un
s:insert({1})
idx:drop()
--- Invalid functional index extractor routine return: the second returned key invalid.
+-- Invalid functional index extractor routine return: invalid return format for multikey index.
lua_code = [[function(tuple) return {"hello", "world"}, {1, 2} end]]
+box.schema.func.create('invalidreturn2', {body = lua_code, is_deterministic = true, is_sandboxed = true})
+idx = s:create_index('idx', {func = box.func.invalidreturn2.id, is_multikey = true, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+s:insert({1})
+idx:drop()
+
+-- Invalid functional index extractor routine return: the second returned key invalid.
+lua_code = [[function(tuple) return {{"hello", "world"}, {1, 2}} end]]
box.schema.func.create('invalidreturn3', {body = lua_code, is_deterministic = true, is_sandboxed = true})
-idx = s:create_index('idx', {func = box.func.invalidreturn3.id, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+idx = s:create_index('idx', {func = box.func.invalidreturn3.id, is_multikey = true, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
+s:insert({1})
+idx:drop()
+
+-- Invalid functional index extractor routine return: multikey return in case of regular index.
+lua_code = [[function(tuple) return {{"hello", "world"}, {1, 2}} end]]
+box.schema.func.create('invalidreturn4', {body = lua_code, is_deterministic = true, is_sandboxed = true})
+idx = s:create_index('idx', {func = box.func.invalidreturn4.id, parts = {{1, 'unsigned'}, {2, 'unsigned'}}})
s:insert({1})
idx:drop()
@@ -102,10 +116,10 @@ collectgarbage()
-- Multikey functional index.
s = box.schema.space.create('withdata', {engine = engine})
-lua_code = [[function(tuple) return {tuple[1] + tuple[2]}, {tuple[1] + tuple[2]}, {tuple[1]} end]]
+lua_code = [[function(tuple) return {{tuple[1] + tuple[2]}, {tuple[1] + tuple[2]}, {tuple[1]}} end]]
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
pk = s:create_index('pk')
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
s:insert({1, 2})
s:insert({3, 5})
s:insert({5, 3})
@@ -120,10 +134,10 @@ box.schema.func.drop('extr')
-- Multikey multipart functional index.
s = box.schema.space.create('withdata', {engine = engine})
-lua_code = [[function(tuple) return {600 + tuple[1], 600 + tuple[2]}, {500 + tuple[1], 500 + tuple[2]} end]]
+lua_code = [[function(tuple) return {{600 + tuple[1], 600 + tuple[2]}, {500 + tuple[1], 500 + tuple[2]}} end]]
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
pk = s:create_index('pk')
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}, {2, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}, {2, 'integer'}}})
s:insert({1, 2})
s:insert({2, 1})
s:insert({3, 3})
@@ -136,10 +150,10 @@ box.schema.func.drop('extr')
-- Multikey non-unique functional index.
s = box.schema.space.create('withdata', {engine = engine})
-lua_code = [[function(tuple) return {500 + tuple[1]}, {500 + tuple[2]}, {500 + tuple[2]} end]]
+lua_code = [[function(tuple) return {{500 + tuple[1]}, {500 + tuple[2]}, {500 + tuple[2]}} end]]
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
pk = s:create_index('pk')
-idx = s:create_index('idx', {unique = false, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = false, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
s:insert({1, 2})
s:insert({2, 1})
idx:select({501})
@@ -176,11 +190,11 @@ lua_code = [[function(tuple)
local address = string.split(tuple[2])
local ret = {}
for _, v in pairs(address) do table.insert(ret, {utf8.upper(v)}) end
- return unpack(ret)
+ return ret
end]]
test_run:cmd("setopt delimiter ''");
box.schema.func.create('addr_extractor', {body = lua_code, is_deterministic = true, is_sandboxed = true})
-idx = s:create_index('addr', {unique = false, func = box.func.addr_extractor.id, parts = {{1, 'string', collation = 'unicode_ci'}}})
+idx = s:create_index('addr', {unique = false, func = box.func.addr_extractor.id, is_multikey = true, parts = {{1, 'string', collation = 'unicode_ci'}}})
idx:select('uk')
idx:select('Sis')
s:drop()
@@ -190,9 +204,9 @@ box.schema.func.drop('addr_extractor')
-- Partial index with functional index extractor
s = box.schema.space.create('withdata', {engine = engine})
pk = s:create_index('pk')
-lua_code = [[function(tuple) if tuple[1] % 2 == 1 then return {tuple[1]} end end]]
+lua_code = [[function(tuple) if tuple[1] % 2 == 1 then return {{tuple[1]}} else return {} end end]]
box.schema.func.create('extr', {body = lua_code, is_deterministic = true, is_sandboxed = true})
-idx = s:create_index('idx', {unique = true, func = box.func.extr.id, parts = {{1, 'integer'}}})
+idx = s:create_index('idx', {unique = true, func = box.func.extr.id, is_multikey = true, parts = {{1, 'integer'}}})
s:insert({1})
s:insert({2})
s:insert({3})
--
2.22.0
More information about the Tarantool-patches
mailing list