From 05fa2f74fb9604bd0d4beb7e86d3145261a7188b Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Thu, 6 Jun 2024 11:27:13 +0300 Subject: [PATCH] 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 64817066ff60 ("vinyl: use update_def index method to update vy_lsm on ddl"). Closes #10095 NO_DOC=bug fix (cherry picked from commit 9b817848c4e87665aa38133f97b86ff6148279a1) --- .../gh-10095-vy-index-update-def-fix.md | 4 +++ src/box/vinyl.c | 16 ++++++++- .../gh_10095_update_index_def_test.lua | 35 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/gh-10095-vy-index-update-def-fix.md create mode 100644 test/vinyl-luatest/gh_10095_update_index_def_test.lua diff --git a/changelogs/unreleased/gh-10095-vy-index-update-def-fix.md b/changelogs/unreleased/gh-10095-vy-index-update-def-fix.md new file mode 100644 index 0000000000..7a40071c99 --- /dev/null +++ b/changelogs/unreleased/gh-10095-vy-index-update-def-fix.md @@ -0,0 +1,4 @@ +## bugfix/vinyl + +* Fixed a bug when a DDL operation crashed in case of extending the key parts + of a secondary index with the primary index key parts (gh-10095). diff --git a/src/box/vinyl.c b/src/box/vinyl.c index cd76d065aa..5ac3d384d3 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -929,6 +929,11 @@ vinyl_index_update_def(struct index *index) { struct vy_lsm *lsm = vy_lsm(index); lsm->opts = index->def->opts; + /* + * Sic: We copy key definitions in-place instead of reallocating them + * because they may be used by read iterators by pointer, for example, + * see vy_run_iterator. + */ key_def_copy(lsm->key_def, index->def->key_def); key_def_copy(lsm->cmp_def, index->def->cmp_def); } @@ -960,7 +965,9 @@ vinyl_index_def_change_requires_rebuild(struct index *index, return true; assert(index_depends_on_pk(index)); + const struct key_def *old_key_def = old_def->key_def; const struct key_def *old_cmp_def = old_def->cmp_def; + const struct key_def *new_key_def = new_def->key_def; const struct key_def *new_cmp_def = new_def->cmp_def; /* @@ -970,8 +977,15 @@ vinyl_index_def_change_requires_rebuild(struct index *index, * won't reveal such statements, but we may still need to * compare them to statements inserted after ALTER hence * we can't narrow field types without index rebuild. + * + * Sic: If secondary index key parts are extended with primary + * index key parts, cmp_def (hence the sorting order) will stay + * the same, but we still have to rebuild the index because + * the new key_def has more parts so we can't update it in-place, + * see vinyl_index_update_def(). */ - if (old_cmp_def->part_count != new_cmp_def->part_count) + if (old_key_def->part_count != new_key_def->part_count || + old_cmp_def->part_count != new_cmp_def->part_count) return true; for (uint32_t i = 0; i < new_cmp_def->part_count; i++) { diff --git a/test/vinyl-luatest/gh_10095_update_index_def_test.lua b/test/vinyl-luatest/gh_10095_update_index_def_test.lua new file mode 100644 index 0000000000..45c93483ae --- /dev/null +++ b/test/vinyl-luatest/gh_10095_update_index_def_test.lua @@ -0,0 +1,35 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group() + +g.before_all(function(cg) + cg.server = server:new() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.test ~= nil then + box.space.test:drop() + end + end) +end) + +g.test_update_index_def = function(cg) + cg.server:exec(function() + local s = box.schema.create_space('test', {engine = 'vinyl'}) + s:create_index('pk') + s:create_index('sk', {parts = {{2, 'unsigned'}}}) + s:insert({1, 10, 100}) + t.assert_equals(s.index.sk:get({10}), {1, 10, 100}) + s.index.sk:alter({parts = {{2, 'unsigned'}, {1, 'unsigned'}}}) + t.assert_equals(s.index.sk:get({10, 1}), {1, 10, 100}) + s.index.sk:alter({parts = {{2, 'unsigned'}, {3, 'unsigned'}}}) + t.assert_equals(s.index.sk:get({10, 100}), {1, 10, 100}) + end) +end -- GitLab