[Tarantool-patches] [PATCH v2 2/2] box: allow upgrading from version 1.6

Serge Petrenko sergepetrenko at tarantool.org
Fri Aug 13 02:30:32 MSK 2021


Direct upgrade support from pre-1.7.5 versions was removed in commit
7d3b80e78206c479cab75b4893629cfa1932252e
(Forbid upgrade from Tarantool < 1.7.5 and refactor upgrade.lua)
The reason for that was the mandatory space format checks introduced
back then. With these space format checks, old schema couldn't be
recovered on new Tarantool versions, because newer versions had
different system space formats. So old schema couldn't be upgraded
because it couldn't even be recovered.

Actually this was rather inconvenient. One had to perform an extra
upgrade step when upgrading from, say, 1.6 to 2.x: instead of
performing a direct upgrade one had to do 1.6 -> 1.10 -> 2.x upgrade
which takes twice the time.

Make it possible to boot from snapshots coming from Tarantool version
1.6.8 and above.

In order to do so, introduce before_replace triggers on system spaces,
which work during snapshot/xlog recovery. The triggers will set tuple
formats to the ones supported by current Tarantool (2.x). This way the
recovered data will have the correct format for a usual schema upgrade.

Also add upgrade_to_1_7_5() handler, which finishes transformation of
old schema to 1.7.5. The handler is fired together with other
box.schema.upgrade() handlers, so there's no user-visible behaviour
change.

Side note: it would be great to use the same technique to allow booting
from pre-1.6.8 snapshots. Unfortunately, this is not possible.

Current triggers don't break the order of schema upgrades, so 1.7.1
upgrades come before 1.7.2 and 1.7.5. This is because all the upgrades
in these versions are replacing existing tuples and not inserting new
ones, so the upgrades may be handled by the before_replace triggers.

Upgrade to 1.6.8 requires inserting new tuples: creating sysviews, like
_vspace, _vuser and so on. This can't be done from the before_replace
triggers, so we would have to run triggers for 1.7.x first which would
allow Tarantool to recover the snapshot, and then run an upgrade handler for
1.6.8. This looks really messy.

Closes #5894
---
 src/box/lua/load_cfg.lua                      |  14 +
 src/box/lua/upgrade.lua                       | 276 +++++++++++-
 test/xlog/gh-5894-pre-1.7.7-upgrade.result    | 400 ++++++++++++++++++
 test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua  |  77 ++++
 .../1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua  |   1 +
 .../1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua  |   1 +
 .../1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua  |   1 +
 .../1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua  |   1 +
 test/xlog/upgrade/fill.lua                    |   4 +
 9 files changed, 773 insertions(+), 2 deletions(-)
 create mode 100644 test/xlog/gh-5894-pre-1.7.7-upgrade.result
 create mode 100644 test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua
 create mode 120000 test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua
 create mode 120000 test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua
 create mode 120000 test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua
 create mode 120000 test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua

diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 4df70c210..7a8cab3fd 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -719,9 +719,23 @@ local function load_cfg(cfg)
             __call = locked(reload_cfg),
         })
 
+    -- Check schema version of the snapshot we're about to recover, if any.
+    -- Some schema versions (below 1.7.5) are incompatible with Tarantool 2.x
+    -- When recovering from such an old snapshot, special recovery triggers on
+    -- system spaces are needed in order to be able to recover and upgrade
+    -- the schema then.
+    local snap_dir = box.cfg.memtx_dir
+    local snap_version = private.get_snapshot_version(snap_dir)
+    if snap_version then
+        private.set_recovery_triggers(snap_version)
+    end
+
     -- This call either succeeds or calls panic() / exit().
     private.cfg_load()
 
+    if snap_version then
+        private.clear_recovery_triggers()
+    end
     -- This block does not raise an error: all necessary checks
     -- already performed in private.cfg_check(). See <dynamic_cfg>
     -- comment.
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 6abce50f4..925adab18 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -1,6 +1,8 @@
 local log = require('log')
 local bit = require('bit')
 local json = require('json')
+local fio = require('fio')
+local xlog = require('xlog')
 
 -- Guest user id - the default user
 local GUEST = 0
@@ -86,6 +88,40 @@ local function set_system_triggers(val)
     foreach_system_space(function(s) s:run_triggers(val) end)
 end
 
+-- Get schema version, stored in _schema system space, by reading the latest
+-- snapshot file from the snap_dir. Useful to define schema_version before
+-- recovering the snapshot, because some schema versions are too old and cannot
+-- be recovered normally.
+local function get_snapshot_version(snap_dir)
+    local snap_pattern = snap_dir..'/'..string.rep('[0-9]', 20)..'.snap'
+    local snap_list = fio.glob(snap_pattern)
+    table.sort(snap_list)
+    local snap = snap_list[#snap_list]
+    if not snap then
+        return nil
+    end
+    local version = nil
+    for _, row in xlog.pairs(snap) do
+        local sid = row.BODY and row.BODY.space_id
+        if sid == box.schema.SCHEMA_ID then
+            local tuple = row.BODY.tuple
+            if tuple and tuple[1] == 'version' then
+                local major, minor, patch = tuple[2], tuple[3], tuple[4] or 0
+                if major and minor and patch and type(major) == 'number' and
+                   type(minor) == 'number' and type(patch) == 'number' then
+                    version = mkversion(major, minor, patch)
+                    break
+                end
+            end
+        elseif sid and sid > box.schema.SCHEMA_ID then
+            -- Exit early if version wasn't found in _schema space.
+            -- Snapshot rows are ordered by space id.
+            break
+        end
+    end
+    return version
+end
+
 --------------------------------------------------------------------------------
 -- Bootstrap
 --------------------------------------------------------------------------------
@@ -131,6 +167,144 @@ local function create_sysview(source_id, target_id)
     end
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 1.7.1
+--------------------------------------------------------------------------------
+local function user_trig_1_7_1(_, tuple)
+    if tuple and tuple[3] == 'guest' and not tuple[5] then
+        local auth_method_list = {}
+        auth_method_list["chap-sha1"] = box.schema.user.password("")
+        tuple = tuple:update{{'=', 5, auth_method_list}}
+        log.info("Set empty password to user 'guest'")
+    end
+    return tuple
+end
+
+--------------------------------------------------------------------------------
+-- Tarantool 1.7.2
+--------------------------------------------------------------------------------
+local function index_trig_1_7_2(_, tuple)
+    local field_types_v16 = {
+        num = 'unsigned',
+        int = 'integer',
+        str = 'string',
+    }
+    if not tuple then
+        return tuple
+    end
+    local parts = tuple[6]
+    local changed = false
+    for _, part in pairs(parts) do
+        local field_type = part[2]:lower()
+        if field_types_v16[field_type] ~= nil then
+            part[2] = field_types_v16[field_type]
+            changed = true
+        end
+    end
+    if changed then
+        log.info("Update index '%s' on space '%s': set parts to %s", tuple[3],
+                 box.space[tuple[1]].name, json.encode(parts))
+        tuple = tuple:update{{'=', 6, parts}}
+    end
+    return tuple
+end
+
+--------------------------------------------------------------------------------
+-- Tarantool 1.7.5
+--------------------------------------------------------------------------------
+local function create_truncate_space()
+    local _truncate = box.space[box.schema.TRUNCATE_ID]
+
+    log.info("create space _truncate")
+    box.space._space:insert{
+        _truncate.id, ADMIN, '_truncate', 'memtx', 0, setmap({}),
+        {{name = 'id', type = 'unsigned'}, {name = 'count', type = 'unsigned'}}
+    }
+
+    log.info("create index primary on _truncate")
+    box.space._index:insert{
+        _truncate.id, 0, 'primary', 'tree', {unique = true}, {{0, 'unsigned'}}
+    }
+
+    local _priv = box.space[box.schema.PRIV_ID]
+    _priv:insert{ADMIN, PUBLIC, 'space', _truncate.id, box.priv.W}
+end
+
+local function upgrade_to_1_7_5()
+    create_truncate_space()
+end
+
+local function user_trig_1_7_5(_, tuple)
+    if tuple and not tuple[5] then
+        tuple = tuple:update{{'=', 5, setmap({})}}
+        log.info("Set empty password to %s '%s'", tuple[4], tuple[3])
+    end
+    return tuple
+end
+
+local space_formats_1_7_5 = {
+    _schema = {
+        {name = 'key', type = 'string'},
+    },
+    _space = {
+        {name = 'id', type = 'unsigned'},
+        {name = 'owner', type = 'unsigned'},
+        {name = 'name', type = 'string'},
+        {name = 'engine', type = 'string'},
+        {name = 'field_count', type = 'unsigned'},
+        {name = 'flags', type = 'map'},
+        {name = 'format', type = 'array'},
+    },
+    _index = {
+        {name = 'id', type = 'unsigned'},
+        {name = 'iid', type = 'unsigned'},
+        {name = 'name', type = 'string'},
+        {name = 'type', type = 'string'},
+        {name = 'opts', type = 'map'},
+        {name = 'parts', type = 'array'},
+    },
+    _func = {
+        {name = 'id', type = 'unsigned'},
+        {name = 'owner', type = 'unsigned'},
+        {name = 'name', type = 'string'},
+        {name = 'setuid', type = 'unsigned'},
+    },
+    _user = {
+        {name = 'id', type = 'unsigned'},
+        {name = 'owner', type = 'unsigned'},
+        {name = 'name', type = 'string'},
+        {name = 'type', type = 'string'},
+        {name = 'auth', type = 'map'},
+    },
+    _priv = {
+        {name = 'grantor', type = 'unsigned'},
+        {name = 'grantee', type = 'unsigned'},
+        {name = 'object_type', type = 'string'},
+        {name = 'object_id', type = 'unsigned'},
+        {name = 'privilege', type = 'unsigned'},
+    },
+    _cluster = {
+        {name = 'id', type = 'unsigned'},
+        {name = 'uuid', type = 'string'},
+    },
+}
+
+space_formats_1_7_5._vspace = space_formats_1_7_5._space
+space_formats_1_7_5._vindex = space_formats_1_7_5._index
+space_formats_1_7_5._vfunc = space_formats_1_7_5._func
+space_formats_1_7_5._vuser = space_formats_1_7_5._user
+space_formats_1_7_5._vpriv = space_formats_1_7_5._priv
+
+local function space_trig_1_7_5(_, tuple)
+    if tuple and space_formats_1_7_5[tuple[3]] and
+       not table.equals(space_formats_1_7_5[tuple[3]], tuple[7]) then
+        tuple = tuple:update{{'=', 7, space_formats_1_7_5[tuple[3]]}}
+        log.info("Update space '%s' format: new format %s", tuple[3],
+                 json.encode(tuple[7]))
+    end
+    return tuple
+end
+
 local function initial_1_7_5()
     -- stick to the following convention:
     -- prefer user id (owner id) in field #1
@@ -452,6 +626,15 @@ local function upgrade_to_1_7_7()
     _priv:replace({ADMIN, SUPER, 'universe', 0, 4294967295})
 end
 
+local function priv_trig_1_7_7(_, tuple)
+    if tuple and tuple[2] == ADMIN and tuple[3] == 'universe' and
+       tuple[5] ~= box.priv.ALL then
+        tuple = tuple:update{{'=', 5, box.priv.ALL}}
+        log.info("Grant all privileges to user 'admin'")
+    end
+    return tuple
+end
+
 --------------------------------------------------------------------------------
 --- Tarantool 1.10.0
 --------------------------------------------------------------------------------
@@ -1021,6 +1204,7 @@ end
 --------------------------------------------------------------------------------
 
 local handlers = {
+    {version = mkversion(1, 7, 5), func = upgrade_to_1_7_5, auto=true},
     {version = mkversion(1, 7, 6), func = upgrade_to_1_7_6, auto = true},
     {version = mkversion(1, 7, 7), func = upgrade_to_1_7_7, auto = true},
     {version = mkversion(1, 10, 0), func = upgrade_to_1_10_0, auto = true},
@@ -1061,13 +1245,98 @@ local function schema_needs_upgrade()
     return false
 end
 
+local trig_oldest_version = nil
+
+-- Some schema changes before version 1.7.7 make it impossible to recover from
+-- older snapshot. The table below consists of before_replace triggers on system
+-- spaces, which make old snapshot schema compatible with current Tarantool
+-- (version 2.x). The triggers replace old format tuples with new ones
+-- in-memory, thus making it possible to recover from a rather old snapshot
+-- (up to schema version 1.6.8). Once the snapshot is recovered, a normal
+-- upgrade procedure may set schema version to the latest one.
+--
+-- The triggers mostly repeat the corresponding upgrade_to_1_7_x functions,
+-- which were used when pre-1.7.x snapshot schema was still recoverable.
+--
+-- When the triggers are used (i.e. when snapshot schema version is below 1.7.5,
+-- the upgrade procedure works as follows:
+-- * first the snapshot is recovered and 1.7.5-compatible schema is applied to
+--   it in-memory with the help of triggers.
+-- * then usual upgrade_to_X_X_X() handlers may be fired to turn schema into the
+--   latest one.
+local recovery_triggers = {
+    {version = mkversion(1, 7, 1), tbl = {
+        _user   = user_trig_1_7_1,
+    }},
+    {version = mkversion(1, 7, 2), tbl = {
+        _index = index_trig_1_7_2,
+    }},
+    {version = mkversion(1, 7, 5), tbl = {
+        _space = space_trig_1_7_5,
+        _user  = user_trig_1_7_5,
+    }},
+    {version = mkversion(1, 7, 7), tbl = {
+        _priv   = priv_trig_1_7_7,
+    }},
+}
+
+-- Once newer schema version is recovered (say, from an xlog following the old
+-- snapshot), the triggers helping recover the old schema should be removed.
+local function schema_trig_last(_, tuple)
+    if tuple and tuple[1] == 'version' then
+        local major, minor, patch = tuple[2], tuple[3], tuple[4] or 0
+        if major and minor and patch and type(major) == 'number' and
+           type(minor) == 'number' and type(patch) == 'number' then
+            local version = mkversion(major, minor, patch)
+            log.info("Recovery trigger: recovered schema version %s. "..
+                     "Removing outdated recovery triggers.", version)
+            box.internal.clear_recovery_triggers(version)
+            trig_oldest_version = version
+        end
+    end
+    return tuple
+end
+
+recovery_triggers[#recovery_triggers].tbl['_schema'] = schema_trig_last
+
+local function on_init_set_recovery_triggers()
+    log.info("Recovering snapshot with schema version %s", trig_oldest_version)
+    for _, trig_tbl in ipairs(recovery_triggers) do
+        if trig_tbl.version > trig_oldest_version then
+            for space, trig in pairs(trig_tbl.tbl) do
+                box.space[space]:before_replace(trig)
+                log.info("Set recovery trigger on space '%s' to comply with "..
+                         "version %s format", space, trig_tbl.version)
+            end
+        end
+    end
+end
+
+local function set_recovery_triggers(version)
+    trig_oldest_version = version
+    box.ctl.on_schema_init(on_init_set_recovery_triggers)
+end
+
+local function clear_recovery_triggers(version)
+    for _, trig_tbl in ipairs(recovery_triggers) do
+        if trig_tbl.version > trig_oldest_version and
+           (not version or trig_tbl.version <= version) then
+            for space, trig in pairs(trig_tbl.tbl) do
+                box.space[space]:before_replace(nil, trig)
+                log.info("Remove recovery trigger on space '%s' for version %s",
+                         space, trig_tbl.version)
+            end
+        end
+    end
+end
+
 local function upgrade(options)
     options = options or {}
     setmetatable(options, {__index = {auto = false}})
 
     local version = get_version()
-    if version < mkversion(1, 7, 5) then
-        log.warn('can upgrade from 1.7.5 only')
+    if version < mkversion(1, 6, 8) then
+        log.warn('can upgrade from 1.6.8 only')
         return
     end
 
@@ -1110,3 +1379,6 @@ end
 box.schema.upgrade = upgrade;
 box.internal.bootstrap = bootstrap;
 box.internal.schema_needs_upgrade = schema_needs_upgrade;
+box.internal.get_snapshot_version = get_snapshot_version;
+box.internal.set_recovery_triggers = set_recovery_triggers;
+box.internal.clear_recovery_triggers = clear_recovery_triggers;
diff --git a/test/xlog/gh-5894-pre-1.7.7-upgrade.result b/test/xlog/gh-5894-pre-1.7.7-upgrade.result
new file mode 100644
index 000000000..aba5a56ab
--- /dev/null
+++ b/test/xlog/gh-5894-pre-1.7.7-upgrade.result
@@ -0,0 +1,400 @@
+-- test-run result file version 2
+test_run = require('test_run').new()
+ | ---
+ | ...
+
+-- Upgrade from 1.6.8.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade"')
+ | ---
+ | - true
+ | ...
+test_run:cmd('start server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:switch('upgrade')
+ | ---
+ | - true
+ | ...
+
+assert(not box.internal.schema_needs_upgrade())
+ | ---
+ | - true
+ | ...
+box.space.distro:select{}
+ | ---
+ | - - ['debian', 'sarge', 31, 1118059200]
+ |   - ['debian', 'etch', 40, 1176033600]
+ |   - ['ubuntu', 'trusty', 1404, 1397736000]
+ |   - ['ubuntu', 'vivid', 1504, 1429790400]
+ |   - ['ubuntu', 'wily', 1510, 1445515200]
+ |   - ['debian', 'wheezy', 70, 1367668800]
+ |   - ['debian', 'squeeze', 60, 1296907200]
+ |   - ['debian', 'lenny', 50, 1234612800]
+ |   - ['debian', 'jessie', 80, 1430049600]
+ |   - ['ubuntu', 'precise', 1510, 1335441600]
+ |   - ['debian', 'woody', 30, 1027080000]
+ | ...
+box.space._index:select{box.space.distro.id}
+ | ---
+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [
+ |         2, 'unsigned']]]
+ |   - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]
+ |   - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]
+ | ...
+box.space._space:format()
+ | ---
+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',
+ |     'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',
+ |     'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]
+ | ...
+box.schema.user.info('admin')
+ | ---
+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('guest')
+ | ---
+ | - - - execute
+ |     - role
+ |     - public
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('someuser')
+ | ---
+ | - - - execute
+ |     - function
+ |     - someotherfunc
+ |   - - execute
+ |     - role
+ |     - public
+ |   - - execute
+ |     - role
+ |     - somerole
+ |   - - read,write,drop,alter
+ |     - space
+ |     - temporary
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.role.info('somerole')
+ | ---
+ | - - - read,write,drop,alter
+ |     - space
+ |     - distro
+ | ...
+
+test_run:switch('default')
+ | ---
+ | - true
+ | ...
+test_run:cmd('stop server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:cmd('delete server upgrade')
+ | ---
+ | - true
+ | ...
+
+-- Upgrade from 1.7.1.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade"')
+ | ---
+ | - true
+ | ...
+test_run:cmd('start server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:switch('upgrade')
+ | ---
+ | - true
+ | ...
+
+assert(not box.internal.schema_needs_upgrade())
+ | ---
+ | - true
+ | ...
+box.space.distro:select{}
+ | ---
+ | - - ['debian', 'etch', 40, 1176033600]
+ |   - ['debian', 'sarge', 31, 1118059200]
+ |   - ['ubuntu', 'wily', 1510, 1445515200]
+ |   - ['ubuntu', 'trusty', 1404, 1397736000]
+ |   - ['ubuntu', 'vivid', 1504, 1429790400]
+ |   - ['debian', 'wheezy', 70, 1367668800]
+ |   - ['debian', 'squeeze', 60, 1296907200]
+ |   - ['debian', 'lenny', 50, 1234612800]
+ |   - ['debian', 'jessie', 80, 1430049600]
+ |   - ['ubuntu', 'precise', 1510, 1335441600]
+ |   - ['debian', 'woody', 30, 1027080000]
+ | ...
+box.space._index:select{box.space.distro.id}
+ | ---
+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [
+ |         2, 'unsigned']]]
+ |   - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]
+ |   - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]
+ | ...
+box.space._space:format()
+ | ---
+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',
+ |     'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',
+ |     'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]
+ | ...
+box.schema.user.info('admin')
+ | ---
+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('guest')
+ | ---
+ | - - - execute
+ |     - role
+ |     - public
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('someuser')
+ | ---
+ | - - - execute
+ |     - function
+ |     - someotherfunc
+ |   - - execute
+ |     - role
+ |     - public
+ |   - - execute
+ |     - role
+ |     - somerole
+ |   - - read,write,drop,alter
+ |     - space
+ |     - temporary
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.role.info('somerole')
+ | ---
+ | - - - read,write,drop,alter
+ |     - space
+ |     - distro
+ | ...
+
+test_run:switch('default')
+ | ---
+ | - true
+ | ...
+test_run:cmd('stop server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:cmd('delete server upgrade')
+ | ---
+ | - true
+ | ...
+
+-- Upgrade from 1.7.2.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade"')
+ | ---
+ | - true
+ | ...
+test_run:cmd('start server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:switch('upgrade')
+ | ---
+ | - true
+ | ...
+
+assert(not box.internal.schema_needs_upgrade())
+ | ---
+ | - true
+ | ...
+box.space.distro:select{}
+ | ---
+ | - - ['debian', 'sarge', 31, 1118059200]
+ |   - ['debian', 'etch', 40, 1176033600]
+ |   - ['ubuntu', 'trusty', 1404, 1397736000]
+ |   - ['ubuntu', 'vivid', 1504, 1429790400]
+ |   - ['debian', 'lenny', 50, 1234612800]
+ |   - ['debian', 'wheezy', 70, 1367668800]
+ |   - ['debian', 'squeeze', 60, 1296907200]
+ |   - ['ubuntu', 'wily', 1510, 1445515200]
+ |   - ['debian', 'jessie', 80, 1430049600]
+ |   - ['ubuntu', 'precise', 1510, 1335441600]
+ |   - ['debian', 'woody', 30, 1027080000]
+ | ...
+box.space._index:select{box.space.distro.id}
+ | ---
+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [
+ |         2, 'unsigned']]]
+ |   - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]
+ |   - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]
+ | ...
+box.space._space:format()
+ | ---
+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',
+ |     'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',
+ |     'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]
+ | ...
+box.schema.user.info('admin')
+ | ---
+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('guest')
+ | ---
+ | - - - execute
+ |     - role
+ |     - public
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('someuser')
+ | ---
+ | - - - execute
+ |     - function
+ |     - someotherfunc
+ |   - - execute
+ |     - role
+ |     - public
+ |   - - execute
+ |     - role
+ |     - somerole
+ |   - - read,write,drop,alter
+ |     - space
+ |     - temporary
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.role.info('somerole')
+ | ---
+ | - - - read,write,drop,alter
+ |     - space
+ |     - distro
+ | ...
+
+test_run:switch('default')
+ | ---
+ | - true
+ | ...
+test_run:cmd('stop server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:cmd('delete server upgrade')
+ | ---
+ | - true
+ | ...
+
+-- Upgrade from 1.7.5.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade"')
+ | ---
+ | - true
+ | ...
+test_run:cmd('start server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:switch('upgrade')
+ | ---
+ | - true
+ | ...
+
+assert(not box.internal.schema_needs_upgrade())
+ | ---
+ | - true
+ | ...
+box.space.distro:select{}
+ | ---
+ | - - ['debian', 'etch', 40, 1176033600]
+ |   - ['debian', 'sarge', 31, 1118059200]
+ |   - ['debian', 'lenny', 50, 1234612800]
+ |   - ['ubuntu', 'trusty', 1404, 1397736000]
+ |   - ['ubuntu', 'vivid', 1504, 1429790400]
+ |   - ['debian', 'wheezy', 70, 1367668800]
+ |   - ['debian', 'squeeze', 60, 1296907200]
+ |   - ['ubuntu', 'wily', 1510, 1445515200]
+ |   - ['debian', 'jessie', 80, 1430049600]
+ |   - ['ubuntu', 'precise', 1510, 1335441600]
+ |   - ['debian', 'woody', 30, 1027080000]
+ | ...
+box.space._index:select{box.space.distro.id}
+ | ---
+ | - - [512, 0, 'primary', 'hash', {'unique': true}, [[0, 'string'], [1, 'string'], [
+ |         2, 'unsigned']]]
+ |   - [512, 1, 'codename', 'hash', {'unique': true}, [[1, 'string']]]
+ |   - [512, 2, 'time', 'tree', {'unique': false}, [[3, 'unsigned']]]
+ | ...
+box.space._space:format()
+ | ---
+ | - [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name',
+ |     'type': 'string'}, {'name': 'engine', 'type': 'string'}, {'name': 'field_count',
+ |     'type': 'unsigned'}, {'name': 'flags', 'type': 'map'}, {'name': 'format', 'type': 'array'}]
+ | ...
+box.schema.user.info('admin')
+ | ---
+ | - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('guest')
+ | ---
+ | - - - execute
+ |     - role
+ |     - public
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.user.info('someuser')
+ | ---
+ | - - - execute
+ |     - function
+ |     - someotherfunc
+ |   - - execute
+ |     - role
+ |     - public
+ |   - - execute
+ |     - role
+ |     - somerole
+ |   - - read,write,drop,alter
+ |     - space
+ |     - temporary
+ |   - - session,usage
+ |     - universe
+ |     - 
+ | ...
+box.schema.role.info('somerole')
+ | ---
+ | - - - read,write,drop,alter
+ |     - space
+ |     - distro
+ | ...
+
+test_run:switch('default')
+ | ---
+ | - true
+ | ...
+test_run:cmd('stop server upgrade')
+ | ---
+ | - true
+ | ...
+test_run:cmd('delete server upgrade')
+ | ---
+ | - true
+ | ...
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
new file mode 100644
index 000000000..9096bcb7a
--- /dev/null
+++ b/test/xlog/gh-5894-pre-1.7.7-upgrade.test.lua
@@ -0,0 +1,77 @@
+test_run = require('test_run').new()
+
+-- Upgrade from 1.6.8.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade"')
+test_run:cmd('start server upgrade')
+test_run:switch('upgrade')
+
+assert(not box.internal.schema_needs_upgrade())
+box.space.distro:select{}
+box.space._index:select{box.space.distro.id}
+box.space._space:format()
+box.schema.user.info('admin')
+box.schema.user.info('guest')
+box.schema.user.info('someuser')
+box.schema.role.info('somerole')
+
+test_run:switch('default')
+test_run:cmd('stop server upgrade')
+test_run:cmd('delete server upgrade')
+
+-- Upgrade from 1.7.1.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade"')
+test_run:cmd('start server upgrade')
+test_run:switch('upgrade')
+
+assert(not box.internal.schema_needs_upgrade())
+box.space.distro:select{}
+box.space._index:select{box.space.distro.id}
+box.space._space:format()
+box.schema.user.info('admin')
+box.schema.user.info('guest')
+box.schema.user.info('someuser')
+box.schema.role.info('somerole')
+
+test_run:switch('default')
+test_run:cmd('stop server upgrade')
+test_run:cmd('delete server upgrade')
+
+-- Upgrade from 1.7.2.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade"')
+test_run:cmd('start server upgrade')
+test_run:switch('upgrade')
+
+assert(not box.internal.schema_needs_upgrade())
+box.space.distro:select{}
+box.space._index:select{box.space.distro.id}
+box.space._space:format()
+box.schema.user.info('admin')
+box.schema.user.info('guest')
+box.schema.user.info('someuser')
+box.schema.role.info('somerole')
+
+test_run:switch('default')
+test_run:cmd('stop server upgrade')
+test_run:cmd('delete server upgrade')
+
+-- Upgrade from 1.7.5.
+test_run:cmd('create server upgrade with script="xlog/upgrade.lua", \
+             workdir="xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade"')
+test_run:cmd('start server upgrade')
+test_run:switch('upgrade')
+
+assert(not box.internal.schema_needs_upgrade())
+box.space.distro:select{}
+box.space._index:select{box.space.distro.id}
+box.space._space:format()
+box.schema.user.info('admin')
+box.schema.user.info('guest')
+box.schema.user.info('someuser')
+box.schema.role.info('somerole')
+
+test_run:switch('default')
+test_run:cmd('stop server upgrade')
+test_run:cmd('delete server upgrade')
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
new file mode 120000
index 000000000..2f2a84962
--- /dev/null
+++ b/test/xlog/upgrade/1.6.8/gh-5894-pre-1.7.7-upgrade/fill.lua
@@ -0,0 +1 @@
+../../fill.lua
\ No newline at end of file
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
new file mode 120000
index 000000000..2f2a84962
--- /dev/null
+++ b/test/xlog/upgrade/1.7.1/gh-5894-pre-1.7.7-upgrade/fill.lua
@@ -0,0 +1 @@
+../../fill.lua
\ No newline at end of file
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
new file mode 120000
index 000000000..2f2a84962
--- /dev/null
+++ b/test/xlog/upgrade/1.7.2/gh-5894-pre-1.7.7-upgrade/fill.lua
@@ -0,0 +1 @@
+../../fill.lua
\ No newline at end of file
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
new file mode 120000
index 000000000..2f2a84962
--- /dev/null
+++ b/test/xlog/upgrade/1.7.5/gh-5894-pre-1.7.7-upgrade/fill.lua
@@ -0,0 +1 @@
+../../fill.lua
\ No newline at end of file
diff --git a/test/xlog/upgrade/fill.lua b/test/xlog/upgrade/fill.lua
index 0ef1a8bb9..310c1ca72 100644
--- a/test/xlog/upgrade/fill.lua
+++ b/test/xlog/upgrade/fill.lua
@@ -56,4 +56,8 @@ end
 box.schema.func.create('someotherfunc')
 box.schema.user.grant('someuser', 'execute', 'function', 'someotherfunc')
 box.schema.user.grant('someuser', 'read,write', 'space', 'temporary')
+
+box.schema.upgrade()
+box.snapshot()
+
 os.exit(0)
-- 
2.30.1 (Apple Git-130)



More information about the Tarantool-patches mailing list