Skip to content

Commit 09b0ce2

Browse files
utils: fix Tarantool version comparison
Before this patch, several features like uuid and fieldpaths support were not supported in Tarantool 3.0.0 because of invalid version comparison. This patch fixes the behavior, as well as introduces more convenient version parsing and comparison utils with new version policy support [1]. 1. https://www.tarantool.io/en/doc/latest/release/policy/
1 parent 6f041ee commit 09b0ce2

File tree

5 files changed

+485
-70
lines changed

5 files changed

+485
-70
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
### Fixed
1111
* Pre-hotreload `cartridge` support (older than 2.4.0) (PR #341).
12+
* Tarantool version-dependent features for 3.x (PR #344).
1213

1314
## [1.0.0] - 02-02-23
1415

crud/common/utils.lua

Lines changed: 165 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ local FilterFieldsError = errors.new_class('FilterFieldsError', {capture_stack =
2222
local NotInitializedError = errors.new_class('NotInitialized')
2323
local StorageInfoError = errors.new_class('StorageInfoError')
2424
local VshardRouterError = errors.new_class('VshardRouterError', {capture_stack = false})
25+
local UtilsInternalError = errors.new_class('UtilsInternalError', {capture_stack = false})
2526
local fiber = require('fiber')
2627

2728
local utils = {}
@@ -294,25 +295,153 @@ function utils.enrich_field_names_with_cmp_key(field_names, key_parts, space_for
294295
return enriched_field_names
295296
end
296297

297-
local enabled_tarantool_features = {}
298298

299-
local function determine_enabled_features()
300-
local major_minor_patch = _G._TARANTOOL:split('-', 1)[1]
301-
local major_minor_patch_parts = major_minor_patch:split('.', 2)
299+
local function get_version_suffix(suffix_candidate)
300+
if type(suffix_candidate) ~= 'string' then
301+
return nil
302+
end
303+
304+
if suffix_candidate:find('^entrypoint$')
305+
or suffix_candidate:find('^alpha%d$')
306+
or suffix_candidate:find('^beta%d$')
307+
or suffix_candidate:find('^rc%d$') then
308+
return suffix_candidate
309+
end
310+
311+
return nil
312+
end
313+
314+
utils.get_version_suffix = get_version_suffix
315+
316+
317+
local suffix_with_digit_weight = {
318+
alpha = -3000,
319+
beta = -2000,
320+
rc = -1000,
321+
}
322+
323+
local function get_version_suffix_weight(suffix)
324+
if suffix == nil then
325+
return 0
326+
end
327+
328+
if suffix:find('^entrypoint$') then
329+
return -math.huge
330+
end
331+
332+
for header, weight in pairs(suffix_with_digit_weight) do
333+
local pos, _, digits = suffix:find('^' .. header .. '(%d)$')
334+
if pos ~= nil then
335+
return weight + tonumber(digits)
336+
end
337+
end
338+
339+
UtilsInternalError:assert(false,
340+
'Unexpected suffix %q, parse with "utils.get_version_suffix" first', suffix)
341+
end
342+
343+
utils.get_version_suffix_weight = get_version_suffix_weight
344+
345+
346+
local function is_version_ge(major, minor,
347+
patch, suffix,
348+
major_to_compare, minor_to_compare,
349+
patch_to_compare, suffix_to_compare)
350+
major = major or 0
351+
minor = minor or 0
352+
patch = patch or 0
353+
local suffix_weight = get_version_suffix_weight(suffix)
354+
355+
major_to_compare = major_to_compare or 0
356+
minor_to_compare = minor_to_compare or 0
357+
patch_to_compare = patch_to_compare or 0
358+
local suffix_weight_to_compare = get_version_suffix_weight(suffix_to_compare)
359+
360+
if major > major_to_compare then return true end
361+
if major < major_to_compare then return false end
302362

363+
if minor > minor_to_compare then return true end
364+
if minor < minor_to_compare then return false end
365+
366+
if patch > patch_to_compare then return true end
367+
if patch < patch_to_compare then return false end
368+
369+
if suffix_weight > suffix_weight_to_compare then return true end
370+
if suffix_weight < suffix_weight_to_compare then return false end
371+
372+
return true
373+
end
374+
375+
utils.is_version_ge = is_version_ge
376+
377+
378+
local function is_version_in_range(major, minor,
379+
patch, suffix,
380+
major_left_side, minor_left_side,
381+
patch_left_side, suffix_left_side,
382+
major_right_side, minor_right_side,
383+
patch_right_side, suffix_right_side)
384+
return is_version_ge(major, minor,
385+
patch, suffix,
386+
major_left_side, minor_left_side,
387+
patch_left_side, suffix_left_side)
388+
and is_version_ge(major_right_side, minor_right_side,
389+
patch_right_side, suffix_right_side,
390+
major, minor,
391+
patch, suffix)
392+
end
393+
394+
utils.is_version_in_range = is_version_in_range
395+
396+
397+
local function get_tarantool_version()
398+
local version_parts = rawget(_G, '_TARANTOOL'):split('-', 1)
399+
400+
local major_minor_patch_parts = version_parts[1]:split('.', 2)
303401
local major = tonumber(major_minor_patch_parts[1])
304402
local minor = tonumber(major_minor_patch_parts[2])
305403
local patch = tonumber(major_minor_patch_parts[3])
306404

307-
-- since Tarantool 2.3
308-
enabled_tarantool_features.fieldpaths = major >= 2 and (minor > 3 or minor == 3 and patch >= 1)
405+
local suffix = get_version_suffix(version_parts[2])
406+
407+
return major, minor, patch, suffix
408+
end
409+
410+
utils.get_tarantool_version = get_tarantool_version
411+
412+
413+
local function tarantool_version_at_least(wanted_major, wanted_minor, wanted_patch)
414+
local major, minor, patch, suffix = get_tarantool_version()
415+
416+
return is_version_ge(major, minor, patch, suffix,
417+
wanted_major, wanted_minor, wanted_patch, nil)
418+
end
419+
420+
utils.tarantool_version_at_least = tarantool_version_at_least
421+
422+
423+
local enabled_tarantool_features = {}
424+
425+
local function determine_enabled_features()
426+
local major, minor, patch, suffix = get_tarantool_version()
427+
428+
-- since Tarantool 2.3.1
429+
enabled_tarantool_features.fieldpaths = is_version_ge(major, minor, patch, suffix,
430+
2, 3, 1, nil)
309431

310-
-- since Tarantool 2.4
311-
enabled_tarantool_features.uuids = major >= 2 and (minor > 4 or minor == 4 and patch >= 1)
432+
-- since Tarantool 2.4.1
433+
enabled_tarantool_features.uuids = is_version_ge(major, minor, patch, suffix,
434+
2, 4, 1, nil)
312435

313436
-- since Tarantool 2.6.3 / 2.7.2 / 2.8.1
314-
enabled_tarantool_features.jsonpath_indexes = major >= 3 or (major >= 2 and ((minor >= 6 and patch >= 3)
315-
or (minor >= 7 and patch >= 2) or (minor >= 8 and patch >= 1) or minor >= 9))
437+
enabled_tarantool_features.jsonpath_indexes = is_version_ge(major, minor, patch, suffix,
438+
2, 8, 1, nil)
439+
or is_version_in_range(major, minor, patch, suffix,
440+
2, 7, 2, nil,
441+
2, 7, math.huge, nil)
442+
or is_version_in_range(major, minor, patch, suffix,
443+
2, 6, 3, nil,
444+
2, 6, math.huge, nil)
316445

317446
-- The merger module was implemented in 2.2.1, see [1].
318447
-- However it had the critical problem [2], which leads to
@@ -322,26 +451,38 @@ local function determine_enabled_features()
322451
--
323452
-- [1]: https://github.com/tarantool/tarantool/issues/3276
324453
-- [2]: https://github.com/tarantool/tarantool/issues/4954
325-
enabled_tarantool_features.builtin_merger =
326-
(major == 2 and minor == 3 and patch >= 3) or
327-
(major == 2 and minor == 4 and patch >= 2) or
328-
(major == 2 and minor == 5 and patch >= 1) or
329-
(major >= 2 and minor >= 6) or
330-
(major >= 3)
454+
enabled_tarantool_features.builtin_merger = is_version_ge(major, minor, patch, suffix,
455+
2, 6, 0, nil)
456+
or is_version_in_range(major, minor, patch, suffix,
457+
2, 5, 1, nil,
458+
2, 5, math.huge, nil)
459+
or is_version_in_range(major, minor, patch, suffix,
460+
2, 4, 2, nil,
461+
2, 4, math.huge, nil)
462+
or is_version_in_range(major, minor, patch, suffix,
463+
2, 3, 3, nil,
464+
2, 3, math.huge, nil)
331465

332466
-- The external merger module leans on a set of relatively
333467
-- new APIs in tarantool. So it works only on tarantool
334468
-- versions, which offer those APIs.
335469
--
336470
-- See README of the module:
337471
-- https://github.com/tarantool/tuple-merger
338-
enabled_tarantool_features.external_merger =
339-
(major == 1 and minor == 10 and patch >= 8) or
340-
(major == 2 and minor == 4 and patch >= 3) or
341-
(major == 2 and minor == 5 and patch >= 2) or
342-
(major == 2 and minor == 6 and patch >= 1) or
343-
(major == 2 and minor >= 7) or
344-
(major >= 3)
472+
enabled_tarantool_features.external_merger = is_version_ge(major, minor, patch, suffix,
473+
2, 7, 0, nil)
474+
or is_version_in_range(major, minor, patch, suffix,
475+
2, 6, 1, nil,
476+
2, 6, math.huge, nil)
477+
or is_version_in_range(major, minor, patch, suffix,
478+
2, 5, 2, nil,
479+
2, 5, math.huge, nil)
480+
or is_version_in_range(major, minor, patch, suffix,
481+
2, 4, 3, nil,
482+
2, 4, math.huge, nil)
483+
or is_version_in_range(major, minor, patch, suffix,
484+
1, 10, 8, nil,
485+
1, 10, math.huge, nil)
345486
end
346487

347488
function utils.tarantool_supports_fieldpaths()
@@ -398,7 +539,7 @@ local function add_nullable_fields_recursive(operations, operations_map, space_f
398539
end
399540

400541
-- Tarantool < 2.1 has no fields `box.error.NO_SUCH_FIELD_NO` and `box.error.NO_SUCH_FIELD_NAME`.
401-
if _TARANTOOL >= "2.1" then
542+
if tarantool_version_at_least(2, 1, 0, nil) then
402543
function utils.is_field_not_found(err_code)
403544
return err_code == box.error.NO_SUCH_FIELD_NO or err_code == box.error.NO_SUCH_FIELD_NAME
404545
end

test/helper.lua

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -314,28 +314,7 @@ function helpers.get_other_storage_bucket_id(cluster, bucket_id)
314314
]], {bucket_id})
315315
end
316316

317-
function helpers.tarantool_version_at_least(wanted_major, wanted_minor,
318-
wanted_patch)
319-
-- Borrowed from `determine_enabled_features()` from
320-
-- crud/common/utils.lua.
321-
local major_minor_patch = _TARANTOOL:split('-', 1)[1]
322-
local major_minor_patch_parts = major_minor_patch:split('.', 2)
323-
324-
local major = tonumber(major_minor_patch_parts[1])
325-
local minor = tonumber(major_minor_patch_parts[2])
326-
local patch = tonumber(major_minor_patch_parts[3])
327-
328-
if major < (wanted_major or 0) then return false end
329-
if major > (wanted_major or 0) then return true end
330-
331-
if minor < (wanted_minor or 0) then return false end
332-
if minor > (wanted_minor or 0) then return true end
333-
334-
if patch < (wanted_patch or 0) then return false end
335-
if patch > (wanted_patch or 0) then return true end
336-
337-
return true
338-
end
317+
helpers.tarantool_version_at_least = crud_utils.tarantool_version_at_least
339318

340319
function helpers.update_sharding_key_cache(cluster, space_name)
341320
return cluster.main_server.net_box:eval([[

test/unit/reverse_inplace_test.lua

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)