@@ -12,7 +12,9 @@ if has_keydef then
12
12
keydef_lib = compat .require (' tuple.keydef' , ' key_def' )
13
13
end
14
14
15
- local select_plan = {}
15
+ local select_plan = {
16
+ _internal = {},
17
+ }
16
18
17
19
local IndexTypeError = errors .new_class (' IndexTypeError' , {capture_stack = false })
18
20
local FilterFieldsError = errors .new_class (' FilterFieldsError' , {capture_stack = false })
@@ -48,49 +50,92 @@ local function get_index_for_condition(space_indexes, space_format, condition)
48
50
end
49
51
end
50
52
51
- local function extract_sharding_key_from_scan_value (scan_value , scan_index , sharding_index )
52
- if # scan_value < # sharding_index .parts then
53
- return nil
54
- end
53
+ function select_plan ._internal .extract_sharding_key_from_conditions (conditions , sharding_index ,
54
+ space_indexes , fieldno_map )
55
+ dev_checks (' table' , ' table' , ' table' , ' table' )
55
56
56
- if scan_index .id == sharding_index .id then
57
- return scan_value
58
- end
57
+ -- If name is both valid index name and field name,
58
+ -- it is interpreted as index name.
59
+ local filled_fields = {}
60
+ for _ , condition in ipairs (conditions ) do
61
+ if condition .operator ~= compare_conditions .operators .EQ then
62
+ goto continue
63
+ end
59
64
60
- local scan_value_fields_values = {}
61
- for i , scan_index_part in ipairs (scan_index .parts ) do
62
- scan_value_fields_values [scan_index_part .fieldno ] = scan_value [i ]
63
- end
65
+ local index = space_indexes [condition .operand ]
66
+ if index ~= nil then
67
+ for i , part in ipairs (index .parts ) do
68
+ -- Consider the following case:
69
+ -- index_0: {'foo', 'bar'},
70
+ -- index_1: {'baz', 'bar'},
71
+ -- conditions: {{'==', 'index_0', {1, 2}}, {'==', 'index_1', {3, nil}}}.
72
+ -- To check that nil parts will not overwrite already filled_fields,
73
+ -- we verify that filled_fields[part.fieldno] is empty. If there are
74
+ -- more than one non-null different value in conditions with equal operator,
75
+ -- request is already in conflict and it doesn't matter what sharding key we
76
+ -- will return.
77
+ if filled_fields [part .fieldno ] == nil then
78
+ filled_fields [part .fieldno ] = condition .values [i ]
79
+ end
80
+ end
64
81
65
- -- check that sharding key is included in the scan index fields
66
- local sharding_key = {}
67
- for _ , sharding_key_part in ipairs (sharding_index .parts ) do
68
- local fieldno = sharding_key_part .fieldno
82
+ goto continue
83
+ end
69
84
70
- -- sharding key isn't included in scan key
71
- if scan_value_fields_values [ fieldno ] == nil then
72
- return nil
85
+ local fieldno = fieldno_map [ condition . operand ]
86
+ if fieldno == nil then
87
+ goto continue
73
88
end
89
+ filled_fields [fieldno ] = condition .values [1 ]
74
90
75
- local field_value = scan_value_fields_values [fieldno ]
91
+ :: continue::
92
+ end
76
93
77
- -- sharding key contains nil values
78
- if field_value == nil then
94
+ local sharding_key = {}
95
+ for i , v in ipairs (sharding_index .parts ) do
96
+ if filled_fields [v .fieldno ] == nil then
79
97
return nil
80
98
end
81
99
82
- table.insert ( sharding_key , field_value )
100
+ sharding_key [ i ] = filled_fields [ v . fieldno ]
83
101
end
84
102
85
103
return sharding_key
86
104
end
87
105
106
+ function select_plan ._internal .get_sharding_key_from_scan_value (scan_value , scan_index , scan_iter , sharding_index )
107
+ dev_checks (' ?' , ' table' , ' number' , ' table' )
108
+
109
+ if scan_value == nil then
110
+ return nil
111
+ end
112
+
113
+ if scan_iter ~= box .index .EQ and scan_iter ~= box .index .REQ then
114
+ return nil
115
+ end
116
+
117
+ if scan_index .id == sharding_index .id then
118
+ if type (scan_value ) ~= ' table' then
119
+ return scan_value
120
+ end
121
+
122
+ for i , _ in ipairs (sharding_index .parts ) do
123
+ if scan_value [i ] == nil then return nil end
124
+ end
125
+ return scan_value
126
+ end
127
+
128
+ return nil
129
+ end
130
+
88
131
-- We need to construct after_tuple by field_names
89
132
-- because if `fields` option is specified we have after_tuple with partial fields
90
133
-- and these fields are ordered by field_names + primary key + scan key
91
134
-- this order can be differ from order in space format
92
135
-- so we need to cast after_tuple to space format for scrolling tuples on storage
93
- local function construct_after_tuple_by_fields (space_format , field_names , tuple )
136
+ local function construct_after_tuple_by_fields (fieldno_map , field_names , tuple )
137
+ dev_checks (' ?table' , ' ?table' , ' ?table|cdata' )
138
+
94
139
if tuple == nil then
95
140
return nil
96
141
end
@@ -99,15 +144,10 @@ local function construct_after_tuple_by_fields(space_format, field_names, tuple)
99
144
return tuple
100
145
end
101
146
102
- local positions = {}
103
147
local transformed_tuple = {}
104
148
105
- for i , field in ipairs (space_format ) do
106
- positions [field .name ] = i
107
- end
108
-
109
149
for i , field_name in ipairs (field_names ) do
110
- local fieldno = positions [field_name ]
150
+ local fieldno = fieldno_map [field_name ]
111
151
if fieldno == nil then
112
152
return nil , FilterFieldsError :new (
113
153
' Space format doesn\' t contain field named %q' , field_name
@@ -145,6 +185,8 @@ function select_plan.new(space, conditions, opts)
145
185
local scan_value
146
186
local scan_condition_num
147
187
188
+ local fieldno_map = utils .get_format_fieldno_map (space_format )
189
+
148
190
-- search index to iterate over
149
191
for i , condition in ipairs (conditions ) do
150
192
scan_index = get_index_for_condition (space_indexes , space_format , condition )
@@ -176,9 +218,7 @@ function select_plan.new(space, conditions, opts)
176
218
177
219
-- handle opts.first
178
220
local total_tuples_count
179
- local scan_after_tuple , err = construct_after_tuple_by_fields (
180
- space_format , field_names , opts .after_tuple
181
- )
221
+ local scan_after_tuple , err = construct_after_tuple_by_fields (fieldno_map , field_names , opts .after_tuple )
182
222
if err ~= nil then
183
223
return nil , err
184
224
end
@@ -230,9 +270,12 @@ function select_plan.new(space, conditions, opts)
230
270
local sharding_index = opts .sharding_key_as_index_obj or primary_index
231
271
232
272
-- get sharding key value
233
- local sharding_key
234
- if scan_value ~= nil and (scan_iter == box .index .EQ or scan_iter == box .index .REQ ) then
235
- sharding_key = extract_sharding_key_from_scan_value (scan_value , scan_index , sharding_index )
273
+ local sharding_key = select_plan ._internal .get_sharding_key_from_scan_value (scan_value , scan_index ,
274
+ scan_iter , sharding_index )
275
+
276
+ if sharding_key == nil then
277
+ sharding_key = select_plan ._internal .extract_sharding_key_from_conditions (conditions , sharding_index ,
278
+ space_indexes , fieldno_map )
236
279
end
237
280
238
281
local plan = {
0 commit comments