<HTML><BODY><div>Thanks for your patch. LGTM but consider several nits below.<br><br> <blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;">Пятница, 13 августа 2021, 2:35 +03:00 от Serge Petrenko via Tarantool-patches <tarantool-patches@dev.tarantool.org>:<br> <div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_16288113010124672442_BODY">Direct upgrade support from pre-1.7.5 versions was removed in commit<br>7d3b80e78206c479cab75b4893629cfa1932252e<br>(Forbid upgrade from Tarantool < 1.7.5 and refactor upgrade.lua)<br>The reason for that was the mandatory space format checks introduced<br>back then. With these space format checks, old schema couldn't be<br>recovered on new Tarantool versions, because newer versions had<br>different system space formats. So old schema couldn't be upgraded<br>because it couldn't even be recovered.<br><br>Actually this was rather inconvenient. One had to perform an extra<br>upgrade step when upgrading from, say, 1.6 to 2.x: instead of<br>performing a direct upgrade one had to do 1.6 -> 1.10 -> 2.x upgrade<br>which takes twice the time.<br><br>Make it possible to boot from snapshots coming from Tarantool version<br>1.6.8 and above.<br><br>In order to do so, introduce before_replace triggers on system spaces,<br>which work during snapshot/xlog recovery. The triggers will set tuple<br>formats to the ones supported by current Tarantool (2.x). This way the<br>recovered data will have the correct format for a usual schema upgrade.<br><br>Also add upgrade_to_1_7_5() handler, which finishes transformation of<br>old schema to 1.7.5. The handler is fired together with other<br>box.schema.upgrade() handlers, so there's no user-visible behaviour<br>change.<br><br>Side note: it would be great to use the same technique to allow booting<br>from pre-1.6.8 snapshots. Unfortunately, this is not possible.<br><br>Current triggers don't break the order of schema upgrades, so 1.7.1<br>upgrades come before 1.7.2 and 1.7.5. This is because all the upgrades<br>in these versions are replacing existing tuples and not inserting new<br>ones, so the upgrades may be handled by the before_replace triggers.<br><br>Upgrade to 1.6.8 requires inserting new tuples: creating sysviews, like<br>_vspace, _vuser and so on. This can't be done from the before_replace<br>triggers, so we would have to run triggers for 1.7.x first which would<br>allow Tarantool to recover the snapshot, and then run an upgrade handler for<br>1.6.8. This looks really messy.<br><br>Closes #5894<br>---<br> src/box/lua/load_cfg.lua | 14 +<br> src/box/lua/upgrade.lua | 276 +++++++++++-<br> test/xlog/gh-5894-pre-1.7.7-upgrade.result | 400 ++++++++++++++++++<br> test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua | 77 ++++<br> .../1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua | 1 +<br> .../1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua | 1 +<br> .../1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua | 1 +<br> .../1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua | 1 +<br> test/xlog/upgrade/fill.lua | 4 +<br> 9 files changed, 773 insertions(+), 2 deletions(-)<br> create mode 100644 test/xlog/gh-5894-pre-1.7.7-upgrade.result<br> create mode 100644 test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua<br> create mode 120000 test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua<br> create mode 120000 test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua<br> create mode 120000 test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua<br> create mode 120000 test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua<br><br>diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua<br>index 4df70c210..7a8cab3fd 100644<br>--- a/src/box/lua/load_cfg.lua<br>+++ b/src/box/lua/load_cfg.lua<br>@@ -719,9 +719,23 @@ local function load_cfg(cfg)<br>             __call = locked(reload_cfg),<br>         })<br> <br>+ -- Check schema version of the snapshot we're about to recover, if any.<br>+ -- Some schema versions (below 1.7.5) are incompatible with Tarantool 2.x<br>+ -- When recovering from such an old snapshot, special recovery triggers on<br>+ -- system spaces are needed in order to be able to recover and upgrade<br>+ -- the schema then.<br>+ local snap_dir = box.cfg.memtx_dir<br>+ local snap_version = private.get_snapshot_version(snap_dir)<br>+ if snap_version then<br>+ private.set_recovery_triggers(snap_version)<br>+ end<br>+<br>     -- This call either succeeds or calls panic() / exit().<br>     private.cfg_load()<br> <br>+ if snap_version then<br>+ private.clear_recovery_triggers()<br>+ end<br>     -- This block does not raise an error: all necessary checks<br>     -- already performed in private.cfg_check(). See <dynamic_cfg><br>     -- comment.<br>diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua<br>index 6abce50f4..925adab18 100644<br>--- a/src/box/lua/upgrade.lua<br>+++ b/src/box/lua/upgrade.lua<br>@@ -1,6 +1,8 @@<br> local log = require('log')<br> local bit = require('bit')<br> local json = require('json')<br>+local fio = require('fio')<br>+local xlog = require('xlog')<br> <br> -- Guest user id - the default user<br> local GUEST = 0<br>@@ -86,6 +88,40 @@ local function set_system_triggers(val)<br>     foreach_system_space(function(s) s:run_triggers(val) end)<br> end<br> <br>+-- Get schema version, stored in _schema system space, by reading the latest<br>+-- snapshot file from the snap_dir. Useful to define schema_version before<br>+-- recovering the snapshot, because some schema versions are too old and cannot<br>+-- be recovered normally.<br>+local function get_snapshot_version(snap_dir)<br>+ local snap_pattern = snap_dir..'/'..string.rep('[0-9]', 20)..'.snap'</div></div></div></div></blockquote></div><div> </div><div>Probably we could use fio.pathjoin here</div><div> </div><div><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div><div class="js-helper js-readmsg-msg"><div><div>+ local snap_list = fio.glob(snap_pattern)<br>+ table.sort(snap_list)<br>+ local snap = snap_list[#snap_list]<br>+ if not snap then<br>+ return nil<br>+ end<br>+ local version = nil<br>+ for _, row in xlog.pairs(snap) do<br>+ local sid = row.BODY and row.BODY.space_id<br>+ if sid == box.schema.SCHEMA_ID then<br>+ local tuple = row.BODY.tuple<br>+ if tuple and tuple[1] == 'version' then<br>+ local major, minor, patch = tuple[2], tuple[3], tuple[4] or 0</div></div></div></div></blockquote></div><div> </div><div>Could it be replaced with tuple:unpack(2, 4)?</div><div> </div><div><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div><div class="js-helper js-readmsg-msg"><div><div>+ if major and minor and patch and type(major) == 'number' and<br>+ type(minor) == 'number' and type(patch) == 'number' then</div></div></div></div></blockquote></div><div> </div><div>Here you use type() == number checks. Could it be different? In case of broken snap?</div><div>So, we should log it I assume. The same for similar places below.</div><div> </div><div><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div><div class="js-helper js-readmsg-msg"><div><div>+ version = mkversion(major, minor, patch)<br>+ break<br>+ end<br>+ end<br>+ elseif sid and sid > box.schema.SCHEMA_ID then<br>+ -- Exit early if version wasn't found in _schema space.<br>+ -- Snapshot rows are ordered by space id.<br>+ break<br>+ end<br>+ end<br>+ return version<br>+end<br>+<br> --------------------------------------------------------------------------------<br> -- Bootstrap<br> --------------------------------------------------------------------------------<br>@@ -131,6 +167,144 @@ local function create_sysview(source_id, target_id)<br>     end<br> end<br> <br>+--------------------------------------------------------------------------------<br>+-- Tarantool 1.7.1<br>+--------------------------------------------------------------------------------<br>+local function user_trig_1_7_1(_, tuple)<br>+ if tuple and tuple[3] == 'guest' and not tuple[5] then</div></div></div></div></blockquote></div><div> </div><div>I think it’s better to use explicit check for tuple[5] value.</div><div>If it’s a boolean value could it be box.NULL?</div><div> </div><div><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div><div class="js-helper js-readmsg-msg"><div><div>+ local auth_method_list = {}<br>+ auth_method_list["chap-sha1"] = box.schema.user.password("")<br>+ tuple = tuple:update{{'=', 5, auth_method_list}}<br>+ log.info("Set empty password to user 'guest'")<br>+ end<br>+ return tuple<br>+end<br>+<br>+--------------------------------------------------------------------------------<br>+-- Tarantool 1.7.2<br>+--------------------------------------------------------------------------------<br>+local function index_trig_1_7_2(_, tuple)<br>+ local field_types_v16 = {<br>+ num = 'unsigned',<br>+ int = 'integer',<br>+ str = 'string',<br>+ }<br>+ if not tuple then<br>+ return tuple<br>+ end<br>+ local parts = tuple[6]<br>+ local changed = false<br>+ for _, part in pairs(parts) do<br>+ local field_type = part[2]:lower()<br>+ if field_types_v16[field_type] ~= nil then<br>+ part[2] = field_types_v16[field_type]<br>+ changed = true<br>+ end<br>+ end<br>+ if changed then<br>+ log.info("Update index '%s' on space '%s': set parts to %s", tuple[3],<br>+ box.space[tuple[1]].name, json.encode(parts))<br>+ tuple = tuple:update{{'=', 6, parts}}<br>+ end<br>+ return tuple<br>+end<br>+<br>+--------------------------------------------------------------------------------<br>+-- Tarantool 1.7.5<br>+--------------------------------------------------------------------------------<br>+local function create_truncate_space()<br>+ local _truncate = box.space[box.schema.TRUNCATE_ID]<br>+<br>+ log.info("create space _truncate")<br>+ box.space._space:insert{<br>+ _truncate.id, ADMIN, '_truncate', 'memtx', 0, setmap({}),<br>+ {{name = 'id', type = 'unsigned'}, {name = 'count', type = 'unsigned'}}<br>+ }<br>+<br>+ log.info("create index primary on _truncate")<br>+ box.space._index:insert{<br>+ _truncate.id, 0, 'primary', 'tree', {unique = true}, {{0, 'unsigned'}}<br>+ }<br>+<br>+ local _priv = box.space[box.schema.PRIV_ID]<br>+ _priv:insert{ADMIN, PUBLIC, 'space', _truncate.id, box.priv.W}<br>+end<br>+<br>+local function upgrade_to_1_7_5()<br>+ create_truncate_space()<br>+end<br>+<br>+local function user_trig_1_7_5(_, tuple)<br>+ if tuple and not tuple[5] then<br>+ tuple = tuple:update{{'=', 5, setmap({})}}<br>+ log.info("Set empty password to %s '%s'", tuple[4], tuple[3])<br>+ end<br>+ return tuple<br>+end<br>+<br>+local space_formats_1_7_5 = {<br>+ _schema = {<br>+ {name = 'key', type = 'string'},<br>+ },<br>+ _space = {<br>+ {name = 'id', type = 'unsigned'},<br>+ {name = 'owner', type = 'unsigned'},<br>+ {name = 'name', type = 'string'},<br>+ {name = 'engine', type = 'string'},<br>+ {name = 'field_count', type = 'unsigned'},<br>+ {name = 'flags', type = 'map'},<br>+ {name = 'format', type = 'array'},<br>+ },<br>+ _index = {<br>+ {name = 'id', type = 'unsigned'},<br>+ {name = 'iid', type = 'unsigned'},<br>+ {name = 'name', type = 'string'},<br>+ {name = 'type', type = 'string'},<br>+ {name = 'opts', type = 'map'},<br>+ {name = 'parts', type = 'array'},<br>+ },<br>+ _func = {<br>+ {name = 'id', type = 'unsigned'},<br>+ {name = 'owner', type = 'unsigned'},<br>+ {name = 'name', type = 'string'},<br>+ {name = 'setuid', type = 'unsigned'},<br>+ },<br>+ _user = {<br>+ {name = 'id', type = 'unsigned'},<br>+ {name = 'owner', type = 'unsigned'},<br>+ {name = 'name', type = 'string'},<br>+ {name = 'type', type = 'string'},<br>+ {name = 'auth', type = 'map'},<br>+ },<br>+ _priv = {<br>+ {name = 'grantor', type = 'unsigned'},<br>+ {name = 'grantee', type = 'unsigned'},<br>+ {name = 'object_type', type = 'string'},<br>+ {name = 'object_id', type = 'unsigned'},<br>+ {name = 'privilege', type = 'unsigned'},<br>+ },<br>+ _cluster = {<br>+ {name = 'id', type = 'unsigned'},<br>+ {name = 'uuid', type = 'string'},<br>+ },<br>+}<br>+<br>+space_formats_1_7_5._vspace = space_formats_1_7_5._space<br>+space_formats_1_7_5._vindex = space_formats_1_7_5._index<br>+space_formats_1_7_5._vfunc = space_formats_1_7_5._func<br>+space_formats_1_7_5._vuser = space_formats_1_7_5._user<br>+space_formats_1_7_5._vpriv = space_formats_1_7_5._priv<br>+<br>+local function space_trig_1_7_5(_, tuple)<br>+ if tuple and space_formats_1_7_5[tuple[3]] and<br>+ not table.equals(space_formats_1_7_5[tuple[3]], tuple[7]) then<br>+ tuple = tuple:update{{'=', 7, space_formats_1_7_5[tuple[3]]}}<br>+ log.info("Update space '%s' format: new format %s", tuple[3],<br>+ json.encode(tuple[7]))<br>+ end<br>+ return tuple<br>+end<br>+<br> local function initial_1_7_5()<br>     -- stick to the following convention:<br>     -- prefer user id (owner id) in field #1<br>@@ -452,6 +626,15 @@ local function upgrade_to_1_7_7()<br>     _priv:replace({ADMIN, SUPER, 'universe', 0, 4294967295})<br> end<br> <br>+local function priv_trig_1_7_7(_, tuple)<br>+ if tuple and tuple[2] == ADMIN and tuple[3] == 'universe' and<br>+ tuple[5] ~= box.priv.ALL then<br>+ tuple = tuple:update{{'=', 5, box.priv.ALL}}<br>+ log.info("Grant all privileges to user 'admin'")<br>+ end<br>+ return tuple<br>+end<br>+<br> --------------------------------------------------------------------------------<br> --- Tarantool 1.10.0<br> --------------------------------------------------------------------------------<br>@@ -1021,6 +1204,7 @@ end<br> --------------------------------------------------------------------------------<br> <br> local handlers = {<br>+ {version = mkversion(1, 7, 5), func = upgrade_to_1_7_5, auto=true},<br>     {version = mkversion(1, 7, 6), func = upgrade_to_1_7_6, auto = true},<br>     {version = mkversion(1, 7, 7), func = upgrade_to_1_7_7, auto = true},<br>     {version = mkversion(1, 10, 0), func = upgrade_to_1_10_0, auto = true},<br>@@ -1061,13 +1245,98 @@ local function schema_needs_upgrade()<br>     return false<br> end<br> <br>+local trig_oldest_version = nil<br>+<br>+-- Some schema changes before version 1.7.7 make it impossible to recover from<br>+-- older snapshot. The table below consists of before_replace triggers on system<br>+-- spaces, which make old snapshot schema compatible with current Tarantool<br>+-- (version 2.x). The triggers replace old format tuples with new ones<br>+-- in-memory, thus making it possible to recover from a rather old snapshot<br>+-- (up to schema version 1.6.8). Once the snapshot is recovered, a normal<br>+-- upgrade procedure may set schema version to the latest one.<br>+--<br>+-- The triggers mostly repeat the corresponding upgrade_to_1_7_x functions,<br>+-- which were used when pre-1.7.x snapshot schema was still recoverable.<br>+--<br>+-- When the triggers are used (i.e. when snapshot schema version is below 1.7.5,<br>+-- the upgrade procedure works as follows:<br>+-- * first the snapshot is recovered and 1.7.5-compatible schema is applied to<br>+-- it in-memory with the help of triggers.<br>+-- * then usual upgrade_to_X_X_X() handlers may be fired to turn schema into the<br>+-- latest one.<br>+local recovery_triggers = {<br>+ {version = mkversion(1, 7, 1), tbl = {<br>+ _user = user_trig_1_7_1,<br>+ }},<br>+ {version = mkversion(1, 7, 2), tbl = {<br>+ _index = index_trig_1_7_2,<br>+ }},<br>+ {version = mkversion(1, 7, 5), tbl = {<br>+ _space = space_trig_1_7_5,<br>+ _user = user_trig_1_7_5,<br>+ }},<br>+ {version = mkversion(1, 7, 7), tbl = {<br>+ _priv = priv_trig_1_7_7,<br>+ }},<br>+}<br>+<br>+-- Once newer schema version is recovered (say, from an xlog following the old<br>+-- snapshot), the triggers helping recover the old schema should be removed.<br>+local function schema_trig_last(_, tuple)<br>+ if tuple and tuple[1] == 'version' then<br>+ local major, minor, patch = tuple[2], tuple[3], tuple[4] or 0<br>+ if major and minor and patch and type(major) == 'number' and<br>+ type(minor) == 'number' and type(patch) == 'number' then<br>+ local version = mkversion(major, minor, patch)<br>+ log.info("Recovery trigger: recovered schema version %s. "..<br>+ "Removing outdated recovery triggers.", version)<br>+ box.internal.clear_recovery_triggers(version)<br>+ trig_oldest_version = version<br>+ end<br>+ end<br>+ return tuple<br>+end<br>+<br>+recovery_triggers[#recovery_triggers].tbl['_schema'] = schema_trig_last<br>+<br>+local function on_init_set_recovery_triggers()<br>+ log.info("Recovering snapshot with schema version %s", trig_oldest_version)<br>+ for _, trig_tbl in ipairs(recovery_triggers) do<br>+ if trig_tbl.version > trig_oldest_version then<br>+ for space, trig in pairs(trig_tbl.tbl) do<br>+ box.space[space]:before_replace(trig)<br>+ log.info("Set recovery trigger on space '%s' to comply with "..<br>+ "version %s format", space, trig_tbl.version)<br>+ end<br>+ end<br>+ end<br>+end<br>+<br>+local function set_recovery_triggers(version)<br>+ trig_oldest_version = version<br>+ box.ctl.on_schema_init(on_init_set_recovery_triggers)<br>+end<br>+<br>+local function clear_recovery_triggers(version)<br>+ for _, trig_tbl in ipairs(recovery_triggers) do<br>+ if trig_tbl.version > trig_oldest_version and<br>+ (not version or trig_tbl.version <= version) then<br>+ for space, trig in pairs(trig_tbl.tbl) do<br>+ box.space[space]:before_replace(nil, trig)<br>+ log.info("Remove recovery trigger on space '%s' for version %s",<br>+ space, trig_tbl.version)<br>+ end<br>+ end<br>+ end<br>+end<br>+<br> local function upgrade(options)<br>     options = options or {}<br>     setmetatable(options, {__index = {auto = false}})<br> <br>     local version = get_version()<br>- if version < mkversion(1, 7, 5) then<br>- log.warn('can upgrade from 1.7.5 only')<br>+ if version < mkversion(1, 6, 8) then<br>+ log.warn('can upgrade from 1.6.8 only')<br>         return<br>     end<br> <br>@@ -1110,3 +1379,6 @@ end<br> box.schema.upgrade = upgrade;<br> box.internal.bootstrap = bootstrap;<br> box.internal.schema_needs_upgrade = schema_needs_upgrade;<br>+box.internal.get_snapshot_version = get_snapshot_version;<br>+box.internal.set_recovery_triggers = set_recovery_triggers;<br>+box.internal.clear_recovery_triggers = clear_recovery_triggers;<br>diff --git a/test/xlog/gh-5894-pre-1.7.7-upgrade.result b/test/xlog/gh-5894-pre-1.7.7-upgrade.result<br>new file mode 100644<br>index 000000000..aba5a56ab<br>--- /dev/null<br>+++ b/test/xlog/gh-5894-pre-1.7.7-upgrade.result<br>@@ -0,0 +1,400 @@<br>+-- test-run result file version 2<br>+test_run = require('test_run').new()<br>+ | ---<br>+ | ...<br>+<br>+-- Upgrade from 1.6.8.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade"')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('start server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:switch('upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+ | ---<br>+ | - true<br>+ | ...<br>+box.space.distro:select{}<br>+ | ---<br>+ | - - ['debian', 'sarge', 31, 1118059200]<br>+ | - ['debian', 'etch', 40, 1176033600]<br>+ | - ['ubuntu', 'trusty', 1404, 1397736000]<br>+ | - ['ubuntu', 'vivid', 1504, 1429790400]<br>+ | - ['ubuntu', 'wily', 1510, 1445515200]<br>+ | - ['debian', 'wheezy', 70, 1367668800]<br>+ | - ['debian', 'squeeze', 60, 1296907200]<br>+ | - ['debian', 'lenny', 50, 1234612800]<br>+ | - ['debian', 'jessie', 80, 1430049600]<br>+ | - ['ubuntu', 'precise', 1510, 1335441600]<br>+ | - ['debian', 'woody', 30, 1027080000]<br>+ | ...<br>+box.space._index:select{box.space.distro.id}<br>+ | ---<br>+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [<br>+ | 2, 'unsigned']]]<br>+ | - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]<br>+ | - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]<br>+ | ...<br>+box.space._space:format()<br>+ | ---<br>+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',<br>+ | 'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',<br>+ | 'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]<br>+ | ...<br>+box.schema.user.info('admin')<br>+ | ---<br>+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('guest')<br>+ | ---<br>+ | - - - execute<br>+ | - role<br>+ | - public<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('someuser')<br>+ | ---<br>+ | - - - execute<br>+ | - function<br>+ | - someotherfunc<br>+ | - - execute<br>+ | - role<br>+ | - public<br>+ | - - execute<br>+ | - role<br>+ | - somerole<br>+ | - - read,write,drop,alter<br>+ | - space<br>+ | - temporary<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.role.info('somerole')<br>+ | ---<br>+ | - - - read,write,drop,alter<br>+ | - space<br>+ | - distro<br>+ | ...<br>+<br>+test_run:switch('default')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('stop server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('delete server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+-- Upgrade from 1.7.1.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade"')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('start server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:switch('upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+ | ---<br>+ | - true<br>+ | ...<br>+box.space.distro:select{}<br>+ | ---<br>+ | - - ['debian', 'etch', 40, 1176033600]<br>+ | - ['debian', 'sarge', 31, 1118059200]<br>+ | - ['ubuntu', 'wily', 1510, 1445515200]<br>+ | - ['ubuntu', 'trusty', 1404, 1397736000]<br>+ | - ['ubuntu', 'vivid', 1504, 1429790400]<br>+ | - ['debian', 'wheezy', 70, 1367668800]<br>+ | - ['debian', 'squeeze', 60, 1296907200]<br>+ | - ['debian', 'lenny', 50, 1234612800]<br>+ | - ['debian', 'jessie', 80, 1430049600]<br>+ | - ['ubuntu', 'precise', 1510, 1335441600]<br>+ | - ['debian', 'woody', 30, 1027080000]<br>+ | ...<br>+box.space._index:select{box.space.distro.id}<br>+ | ---<br>+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [<br>+ | 2, 'unsigned']]]<br>+ | - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]<br>+ | - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]<br>+ | ...<br>+box.space._space:format()<br>+ | ---<br>+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',<br>+ | 'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',<br>+ | 'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]<br>+ | ...<br>+box.schema.user.info('admin')<br>+ | ---<br>+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('guest')<br>+ | ---<br>+ | - - - execute<br>+ | - role<br>+ | - public<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('someuser')<br>+ | ---<br>+ | - - - execute<br>+ | - function<br>+ | - someotherfunc<br>+ | - - execute<br>+ | - role<br>+ | - public<br>+ | - - execute<br>+ | - role<br>+ | - somerole<br>+ | - - read,write,drop,alter<br>+ | - space<br>+ | - temporary<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.role.info('somerole')<br>+ | ---<br>+ | - - - read,write,drop,alter<br>+ | - space<br>+ | - distro<br>+ | ...<br>+<br>+test_run:switch('default')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('stop server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('delete server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+-- Upgrade from 1.7.2.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade"')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('start server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:switch('upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+ | ---<br>+ | - true<br>+ | ...<br>+box.space.distro:select{}<br>+ | ---<br>+ | - - ['debian', 'sarge', 31, 1118059200]<br>+ | - ['debian', 'etch', 40, 1176033600]<br>+ | - ['ubuntu', 'trusty', 1404, 1397736000]<br>+ | - ['ubuntu', 'vivid', 1504, 1429790400]<br>+ | - ['debian', 'lenny', 50, 1234612800]<br>+ | - ['debian', 'wheezy', 70, 1367668800]<br>+ | - ['debian', 'squeeze', 60, 1296907200]<br>+ | - ['ubuntu', 'wily', 1510, 1445515200]<br>+ | - ['debian', 'jessie', 80, 1430049600]<br>+ | - ['ubuntu', 'precise', 1510, 1335441600]<br>+ | - ['debian', 'woody', 30, 1027080000]<br>+ | ...<br>+box.space._index:select{box.space.distro.id}<br>+ | ---<br>+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [<br>+ | 2, 'unsigned']]]<br>+ | - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]<br>+ | - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]<br>+ | ...<br>+box.space._space:format()<br>+ | ---<br>+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',<br>+ | 'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',<br>+ | 'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]<br>+ | ...<br>+box.schema.user.info('admin')<br>+ | ---<br>+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('guest')<br>+ | ---<br>+ | - - - execute<br>+ | - role<br>+ | - public<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('someuser')<br>+ | ---<br>+ | - - - execute<br>+ | - function<br>+ | - someotherfunc<br>+ | - - execute<br>+ | - role<br>+ | - public<br>+ | - - execute<br>+ | - role<br>+ | - somerole<br>+ | - - read,write,drop,alter<br>+ | - space<br>+ | - temporary<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.role.info('somerole')<br>+ | ---<br>+ | - - - read,write,drop,alter<br>+ | - space<br>+ | - distro<br>+ | ...<br>+<br>+test_run:switch('default')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('stop server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('delete server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+-- Upgrade from 1.7.5.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade"')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('start server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:switch('upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+ | ---<br>+ | - true<br>+ | ...<br>+box.space.distro:select{}<br>+ | ---<br>+ | - - ['debian', 'etch', 40, 1176033600]<br>+ | - ['debian', 'sarge', 31, 1118059200]<br>+ | - ['debian', 'lenny', 50, 1234612800]<br>+ | - ['ubuntu', 'trusty', 1404, 1397736000]<br>+ | - ['ubuntu', 'vivid', 1504, 1429790400]<br>+ | - ['debian', 'wheezy', 70, 1367668800]<br>+ | - ['debian', 'squeeze', 60, 1296907200]<br>+ | - ['ubuntu', 'wily', 1510, 1445515200]<br>+ | - ['debian', 'jessie', 80, 1430049600]<br>+ | - ['ubuntu', 'precise', 1510, 1335441600]<br>+ | - ['debian', 'woody', 30, 1027080000]<br>+ | ...<br>+box.space._index:select{box.space.distro.id}<br>+ | ---<br>+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [<br>+ | 2, 'unsigned']]]<br>+ | - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]<br>+ | - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]<br>+ | ...<br>+box.space._space:format()<br>+ | ---<br>+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',<br>+ | 'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',<br>+ | 'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]<br>+ | ...<br>+box.schema.user.info('admin')<br>+ | ---<br>+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('guest')<br>+ | ---<br>+ | - - - execute<br>+ | - role<br>+ | - public<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.user.info('someuser')<br>+ | ---<br>+ | - - - execute<br>+ | - function<br>+ | - someotherfunc<br>+ | - - execute<br>+ | - role<br>+ | - public<br>+ | - - execute<br>+ | - role<br>+ | - somerole<br>+ | - - read,write,drop,alter<br>+ | - space<br>+ | - temporary<br>+ | - - session,usage<br>+ | - universe<br>+ | -<br>+ | ...<br>+box.schema.role.info('somerole')<br>+ | ---<br>+ | - - - read,write,drop,alter<br>+ | - space<br>+ | - distro<br>+ | ...<br>+<br>+test_run:switch('default')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('stop server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>+test_run:cmd('delete server upgrade')<br>+ | ---<br>+ | - true<br>+ | ...<br>diff --git a/test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua b/test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua<br>new file mode 100644<br>index 000000000..9096bcb7a<br>--- /dev/null<br>+++ b/test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua<br>@@ -0,0 +1,77 @@<br>+test_run = require('test_run').new()<br>+<br>+-- Upgrade from 1.6.8.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade"')<br>+test_run:cmd('start server upgrade')<br>+test_run:switch('upgrade')<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+box.space.distro:select{}<br>+box.space._index:select{box.space.distro.id}<br>+box.space._space:format()<br>+box.schema.user.info('admin')<br>+box.schema.user.info('guest')<br>+box.schema.user.info('someuser')<br>+box.schema.role.info('somerole')<br>+<br>+test_run:switch('default')<br>+test_run:cmd('stop server upgrade')<br>+test_run:cmd('delete server upgrade')<br>+<br>+-- Upgrade from 1.7.1.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade"')<br>+test_run:cmd('start server upgrade')<br>+test_run:switch('upgrade')<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+box.space.distro:select{}<br>+box.space._index:select{box.space.distro.id}<br>+box.space._space:format()<br>+box.schema.user.info('admin')<br>+box.schema.user.info('guest')<br>+box.schema.user.info('someuser')<br>+box.schema.role.info('somerole')<br>+<br>+test_run:switch('default')<br>+test_run:cmd('stop server upgrade')<br>+test_run:cmd('delete server upgrade')<br>+<br>+-- Upgrade from 1.7.2.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade"')<br>+test_run:cmd('start server upgrade')<br>+test_run:switch('upgrade')<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+box.space.distro:select{}<br>+box.space._index:select{box.space.distro.id}<br>+box.space._space:format()<br>+box.schema.user.info('admin')<br>+box.schema.user.info('guest')<br>+box.schema.user.info('someuser')<br>+box.schema.role.info('somerole')<br>+<br>+test_run:switch('default')<br>+test_run:cmd('stop server upgrade')<br>+test_run:cmd('delete server upgrade')<br>+<br>+-- Upgrade from 1.7.5.<br>+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \<br>+ workdir="xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade"')<br>+test_run:cmd('start server upgrade')<br>+test_run:switch('upgrade')<br>+<br>+assert(not box.internal.schema_needs_upgrade())<br>+box.space.distro:select{}<br>+box.space._index:select{box.space.distro.id}<br>+box.space._space:format()<br>+box.schema.user.info('admin')<br>+box.schema.user.info('guest')<br>+box.schema.user.info('someuser')<br>+box.schema.role.info('somerole')<br>+<br>+test_run:switch('default')<br>+test_run:cmd('stop server upgrade')<br>+test_run:cmd('delete server upgrade')<br>diff --git a/test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua b/test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua<br>new file mode 120000<br>index 000000000..2f2a84962<br>--- /dev/null<br>+++ b/test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua<br>@@ -0,0 +1 @@<br>+../../fill.lua<br>\ No newline at end of file<br>diff --git a/test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua b/test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua<br>new file mode 120000<br>index 000000000..2f2a84962<br>--- /dev/null<br>+++ b/test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua<br>@@ -0,0 +1 @@<br>+../../fill.lua<br>\ No newline at end of file<br>diff --git a/test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua b/test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua<br>new file mode 120000<br>index 000000000..2f2a84962<br>--- /dev/null<br>+++ b/test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua<br>@@ -0,0 +1 @@<br>+../../fill.lua<br>\ No newline at end of file<br>diff --git a/test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua b/test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua<br>new file mode 120000<br>index 000000000..2f2a84962<br>--- /dev/null<br>+++ b/test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua<br>@@ -0,0 +1 @@<br>+../../fill.lua<br>\ No newline at end of file<br>diff --git a/test/xlog/upgrade/fill.lua b/test/xlog/upgrade/fill.lua<br>index 0ef1a8bb9..310c1ca72 100644<br>--- a/test/xlog/upgrade/fill.lua<br>+++ b/test/xlog/upgrade/fill.lua<br>@@ -56,4 +56,8 @@ end<br> box.schema.func.create('someotherfunc')<br> box.schema.user.grant('someuser', 'execute', 'function', 'someotherfunc')<br> box.schema.user.grant('someuser', 'read,write', 'space', 'temporary')<br>+<br>+box.schema.upgrade()<br>+box.snapshot()<br>+<br> os.exit(0)<br>--<br>2.30.1 (Apple Git-130)</div></div></div></div></blockquote> <div> </div><div data-signature-widget="container"><div data-signature-widget="content"><div>--<br>Oleg Babin</div></div></div><div> </div></div></BODY></HTML>