Skip to content

Commit 9b81784

Browse files
committed
vinyl: fix crash on extending secondary key parts with primary
If a secondary index is altered in such a way that its key parts are extended with the primary key parts, rebuild isn't required because `cmp_def` doesn't change, see `vinyl_index_def_change_requires_rebuild`. In this case `vinyl_index_update_def` will try to update `key_def` and `cmp_def` in-place with `key_def_copy`. This will lead to a crash because the number of parts in the new `key_def` is greater. We can't use `key_def_dup` instead of `key_def_copy` there because there may be read iterators using the old `key_def` by pointer so there's no other option but to force rebuild in this case. The bug was introduced in commit 6481706 ("vinyl: use update_def index method to update vy_lsm on ddl"). Closes tarantool#10095 NO_DOC=bug fix
1 parent 90f3304 commit 9b81784

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## bugfix/vinyl
2+
3+
* Fixed a bug when a DDL operation crashed in case of extending the key parts
4+
of a secondary index with the primary index key parts (gh-10095).

src/box/vinyl.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,11 @@ vinyl_index_update_def(struct index *index)
923923
{
924924
struct vy_lsm *lsm = vy_lsm(index);
925925
lsm->opts = index->def->opts;
926+
/*
927+
* Sic: We copy key definitions in-place instead of reallocating them
928+
* because they may be used by read iterators by pointer, for example,
929+
* see vy_run_iterator.
930+
*/
926931
key_def_copy(lsm->key_def, index->def->key_def);
927932
key_def_copy(lsm->cmp_def, index->def->cmp_def);
928933
}
@@ -954,7 +959,9 @@ vinyl_index_def_change_requires_rebuild(struct index *index,
954959
return true;
955960

956961
assert(index_depends_on_pk(index));
962+
const struct key_def *old_key_def = old_def->key_def;
957963
const struct key_def *old_cmp_def = old_def->cmp_def;
964+
const struct key_def *new_key_def = new_def->key_def;
958965
const struct key_def *new_cmp_def = new_def->cmp_def;
959966

960967
/*
@@ -964,8 +971,15 @@ vinyl_index_def_change_requires_rebuild(struct index *index,
964971
* won't reveal such statements, but we may still need to
965972
* compare them to statements inserted after ALTER hence
966973
* we can't narrow field types without index rebuild.
974+
*
975+
* Sic: If secondary index key parts are extended with primary
976+
* index key parts, cmp_def (hence the sorting order) will stay
977+
* the same, but we still have to rebuild the index because
978+
* the new key_def has more parts so we can't update it in-place,
979+
* see vinyl_index_update_def().
967980
*/
968-
if (old_cmp_def->part_count != new_cmp_def->part_count)
981+
if (old_key_def->part_count != new_key_def->part_count ||
982+
old_cmp_def->part_count != new_cmp_def->part_count)
969983
return true;
970984

971985
for (uint32_t i = 0; i < new_cmp_def->part_count; i++) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
local server = require('luatest.server')
2+
local t = require('luatest')
3+
4+
local g = t.group()
5+
6+
g.before_all(function(cg)
7+
cg.server = server:new()
8+
cg.server:start()
9+
end)
10+
11+
g.after_all(function(cg)
12+
cg.server:drop()
13+
end)
14+
15+
g.after_each(function(cg)
16+
cg.server:exec(function()
17+
if box.space.test ~= nil then
18+
box.space.test:drop()
19+
end
20+
end)
21+
end)
22+
23+
g.test_update_index_def = function(cg)
24+
cg.server:exec(function()
25+
local s = box.schema.create_space('test', {engine = 'vinyl'})
26+
s:create_index('pk')
27+
s:create_index('sk', {parts = {{2, 'unsigned'}}})
28+
s:insert({1, 10, 100})
29+
t.assert_equals(s.index.sk:get({10}), {1, 10, 100})
30+
s.index.sk:alter({parts = {{2, 'unsigned'}, {1, 'unsigned'}}})
31+
t.assert_equals(s.index.sk:get({10, 1}), {1, 10, 100})
32+
s.index.sk:alter({parts = {{2, 'unsigned'}, {3, 'unsigned'}}})
33+
t.assert_equals(s.index.sk:get({10, 100}), {1, 10, 100})
34+
end)
35+
end

0 commit comments

Comments
 (0)