Skip to content

Commit 3c4fb44

Browse files
authored
Fixed select crash when dropping indexes. (#108)
1 parent 291c2fe commit 3c4fb44

File tree

3 files changed

+139
-10
lines changed

3 files changed

+139
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
* Support for UUID field types and UUID values
1313

14+
### Fixed
15+
16+
* Fixed select crash when dropping indexes.
17+
1418
## [0.4.0] - 2020-12-02
1519

1620
### Fixed

crud/select/plan.lua

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,28 @@ local function index_is_allowed(index)
1515
end
1616

1717
local function get_index_for_condition(space_indexes, space_format, condition)
18-
for i= 0, #space_indexes do
18+
-- If we use # (not table.maxn), we may lose indexes, when user drop some indexes.
19+
-- E.g: we have table with indexes id {1, 2, 3, nil, nil, 6}.
20+
-- If we use #{1, 2, 3, nil, nil, 6} (== 3) we will lose index with id = 6.
21+
-- See details: https://github.com/tarantool/crud/issues/103
22+
local max_index = table.maxn(space_indexes)
23+
for i = 0, max_index do
1924
local index = space_indexes[i]
20-
if index.name == condition.operand and index_is_allowed(index) then
21-
return index
25+
if index ~= nil then
26+
if index.name == condition.operand and index_is_allowed(index) then
27+
return index
28+
end
2229
end
2330
end
2431

25-
for i = 0, #space_indexes do
32+
for i = 0, max_index do
2633
local index = space_indexes[i]
27-
local first_part_fieldno = index.parts[1].fieldno
28-
local first_part_name = space_format[first_part_fieldno].name
29-
if first_part_name == condition.operand and index_is_allowed(index) then
30-
return index
34+
if index ~= nil then
35+
local first_part_fieldno = index.parts[1].fieldno
36+
local first_part_name = space_format[first_part_fieldno].name
37+
if first_part_name == condition.operand and index_is_allowed(index) then
38+
return index
39+
end
3140
end
3241
end
3342
end
@@ -39,9 +48,16 @@ local function validate_conditions(conditions, space_indexes, space_format)
3948
end
4049

4150
local index_names = {}
42-
for i = 0, #space_indexes do
51+
52+
-- If we use # (not table.maxn), we may lose indexes, when user drop some indexes.
53+
-- E.g: we have table with indexes id {1, 2, 3, nil, nil, 6}.
54+
-- If we use #{1, 2, 3, nil, nil, 6} (== 3) we will lose index with id = 6.
55+
-- See details: https://github.com/tarantool/crud/issues/103
56+
for i = 0, table.maxn(space_indexes) do
4357
local index = space_indexes[i]
44-
index_names[index.name] = true
58+
if index ~= nil then
59+
index_names[index.name] = true
60+
end
4561
end
4662

4763
for _, condition in ipairs(conditions) do
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
local select_plan = require('crud.select.plan')
2+
3+
local select_conditions = require('crud.select.conditions')
4+
local cond_funcs = select_conditions.funcs
5+
6+
local t = require('luatest')
7+
local g = t.group('select_dropped_indexes')
8+
9+
local helpers = require('test.helper')
10+
11+
g.before_all = function()
12+
helpers.box_cfg()
13+
14+
local customers = box.schema.space.create('customers', {
15+
format = {
16+
{'id', 'unsigned'},
17+
{'bucket_id', 'unsigned'},
18+
{'name', 'string'},
19+
{'age', 'unsigned'},
20+
{'number_of_pets', 'unsigned'},
21+
{'cars', 'array'},
22+
},
23+
if_not_exists = true,
24+
})
25+
26+
customers:create_index('index1', {
27+
type = 'TREE',
28+
parts = {'id'},
29+
if_not_exists = true,
30+
})
31+
32+
customers:create_index('index2', {
33+
parts = {'bucket_id'},
34+
unique = false,
35+
if_not_exists = true,
36+
})
37+
38+
customers:create_index('index3', {
39+
type = 'TREE',
40+
parts = {'age'},
41+
unique = false,
42+
if_not_exists = true,
43+
})
44+
45+
customers:create_index('index4_dropped', {
46+
type = 'HASH',
47+
parts = {'name'},
48+
if_not_exists = true,
49+
})
50+
51+
customers:create_index('index5_dropped', {
52+
type = 'HASH',
53+
parts = {'age'},
54+
if_not_exists = true,
55+
})
56+
57+
customers:create_index('index6', {
58+
type = 'TREE',
59+
parts = {'name'},
60+
unique = false,
61+
if_not_exists = true,
62+
})
63+
64+
customers.index.index4_dropped:drop()
65+
customers.index.index5_dropped:drop()
66+
67+
-- We need this check to make sure that test actually covers a problem.
68+
t.assert(#box.space.customers.index ~= table.maxn(box.space.customers.index))
69+
end
70+
71+
g.after_all = function()
72+
box.space.customers:drop()
73+
end
74+
75+
g.test_before_dropped_index_field = function()
76+
local conditions = { cond_funcs.eq('index3', 20) }
77+
local plan, err = select_plan.new(box.space.customers, conditions)
78+
79+
t.assert_equals(err, nil)
80+
t.assert_type(plan, 'table')
81+
82+
t.assert_equals(plan.conditions, conditions)
83+
t.assert_equals(plan.space_name, 'customers')
84+
t.assert_equals(plan.index_id, 2)
85+
end
86+
87+
g.test_after_dropped_index_field = function()
88+
local conditions = { cond_funcs.eq('index6', 'Alexey') }
89+
local plan, err = select_plan.new(box.space.customers, conditions)
90+
91+
t.assert_equals(err, nil)
92+
t.assert_type(plan, 'table')
93+
94+
t.assert_equals(plan.conditions, conditions)
95+
t.assert_equals(plan.space_name, 'customers')
96+
t.assert_equals(plan.index_id, 5)
97+
end
98+
99+
g.test_non_indexed_field = function()
100+
local conditions = { cond_funcs.eq('number_of_pets', 2) }
101+
local plan, err = select_plan.new(box.space.customers, conditions)
102+
103+
t.assert_equals(err, nil)
104+
t.assert_type(plan, 'table')
105+
106+
t.assert_equals(plan.conditions, conditions)
107+
t.assert_equals(plan.space_name, 'customers')
108+
t.assert_equals(plan.index_id, 0) -- PK
109+
end

0 commit comments

Comments
 (0)