diff --git a/changelogs/unreleased/gh-7149-forbid-ddl-until-box-schema-upgrade.md b/changelogs/unreleased/gh-7149-forbid-ddl-until-box-schema-upgrade.md new file mode 100644 index 0000000000000000000000000000000000000000..1cce7e07d220774fc0f3cc03a8bcd88622d6e6ba --- /dev/null +++ b/changelogs/unreleased/gh-7149-forbid-ddl-until-box-schema-upgrade.md @@ -0,0 +1,3 @@ +## bugfix/core + +* Forbidden DDL operations for the non-upgraded schema (gh-7149). diff --git a/src/box/errcode.h b/src/box/errcode.h index 5b72c1879b6af24582f82666b9673f6341316b84..4807745830d4aab32960db19c443408558dd4f57 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -300,6 +300,7 @@ struct errcode_record { /*245 */_(ER_OLD_TERM, "The term is outdated: old - %llu, new - %llu") \ /*246 */_(ER_INTERFERING_ELECTIONS, "Interfering elections started")\ /*247 */_(ER_ITERATOR_POSITION, "Iterator position is invalid") \ + /*248 */_(ER_DDL_NOT_ALLOWED, "DDL operations are not allowed: %s") \ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index 65587f7843f615afed4b4b1a354c5615a5e208a7..ad5e4908b8552c452231e537affe4645fabcaf40 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -979,12 +979,8 @@ local function load_cfg(cfg) -- Check if schema version matches Tarantool version and print -- warning if it's not (in case user forgot to call -- box.schema.upgrade()). - local needs, schema_version_str = private.schema_needs_upgrade() + local needs, msg = private.schema_needs_upgrade() if needs then - local msg = string.format( - 'Your schema version is %s while Tarantool %s requires a more'.. - ' recent schema version. Please, consider using box.'.. - 'schema.upgrade().', schema_version_str, box.info.version) log.warn(msg) end end diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index c12e9d28986e7fbd257a51c7146a8218a2f36104..992effa4eaa04f3e182795ae5022d0dd9710c2bb 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -241,6 +241,14 @@ local function revoke_object_privs(object_type, object_id) end end +-- Check if schema version matches Tarantool version and raise an error if not. +local function check_schema_version() + local needs_upgrade, msg = internal.schema_needs_upgrade() + if needs_upgrade then + box.error(box.error.DDL_NOT_ALLOWED, msg) + end +end + -- Same as type(), but returns 'number' if 'param' is -- of type 'cdata' and represents a 64-bit integer. local function param_type(param) @@ -797,6 +805,7 @@ box.schema.space.create = function(name, options) temporary = false, } check_param_table(options, options_template) + check_schema_version() options = update_param_table(options, options_defaults) if options.engine == 'vinyl' then options = update_param_table(options, { @@ -885,6 +894,7 @@ box.schema.space.drop = function(space_id, space_name, opts) check_param(space_id, 'space_id', 'number') opts = opts or {} check_param_table(opts, { if_exists = 'boolean' }) + check_schema_version() local _space = box.space[box.schema.SPACE_ID] local _index = box.space[box.schema.INDEX_ID] local _trigger = box.space[box.schema.TRIGGER_ID] @@ -956,6 +966,7 @@ box.schema.space.alter = function(space_id, options) box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id)) end check_param_table(options, alter_space_template) + check_schema_version() local _space = box.space._space local tuple = _space:get({space.id}) @@ -1527,6 +1538,7 @@ box.schema.index.create = function(space_id, name, options) check_param(space_id, 'space_id', 'number') check_param(name, 'name', 'string') check_param_table(options, create_index_template) + check_schema_version() local space = box.space[space_id] if not space then box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id)) @@ -1656,6 +1668,7 @@ end box.schema.index.drop = function(space_id, index_id) check_param(space_id, 'space_id', 'number') check_param(index_id, 'index_id', 'number') + check_schema_version() if index_id == 0 then local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID] local sequence_tuple = _space_sequence:delete{space_id} @@ -1696,6 +1709,7 @@ box.schema.index.alter = function(space_id, index_id, options) end check_param_table(options, alter_index_template) + check_schema_version() if type(space_id) ~= "number" then space_id = space.id @@ -2885,6 +2899,7 @@ box.schema.sequence.create = function(name, opts) opts = opts or {} check_param(name, 'name', 'string') check_param_table(opts, create_sequence_options) + check_schema_version() local ascending = not opts.step or opts.step > 0 local options_defaults = { step = 1, @@ -2910,6 +2925,7 @@ end box.schema.sequence.alter = function(name, opts) check_param_table(opts, alter_sequence_options) + check_schema_version() local id, tuple = sequence_resolve(name) if id == nil then box.error(box.error.NO_SUCH_SEQUENCE, name) @@ -2929,6 +2945,7 @@ end box.schema.sequence.drop = function(name, opts) opts = opts or {} check_param_table(opts, {if_exists = 'boolean'}) + check_schema_version() local id = sequence_resolve(name) if id == nil then if not opts.if_exists then @@ -3171,6 +3188,7 @@ box.schema.func.create = function(name, opts) comment = 'string', param_list = 'table', returns = 'string', exports = 'table', opts = 'table' }) + check_schema_version() local _func = box.space[box.schema.FUNC_ID] local _vfunc = box.space[box.schema.VFUNC_ID] local func = _vfunc.index.name:get{name} @@ -3206,6 +3224,7 @@ end box.schema.func.drop = function(name, opts) opts = opts or {} check_param_table(opts, { if_exists = 'boolean' }) + check_schema_version() local _func = box.space[box.schema.FUNC_ID] local _vfunc = box.space[box.schema.VFUNC_ID] local fid diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua index 12078fc2183bb28493d41c8c525d19771e7397b3..510ce27e26dabfae479c9af224b58cdacabdd775 100644 --- a/src/box/lua/upgrade.lua +++ b/src/box/lua/upgrade.lua @@ -667,6 +667,7 @@ end local function upgrade_priv_to_1_10_2() local _priv = box.space._priv local _vpriv = box.space._vpriv + local _index = box.space._index local format = _priv:format() format[4].type = 'scalar' @@ -674,10 +675,15 @@ local function upgrade_priv_to_1_10_2() format = _vpriv:format() format[4].type = 'scalar' _vpriv:format(format) - _priv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} - _vpriv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} - _priv.index.object:alter{parts={3, 'string', 4, 'scalar'}} - _vpriv.index.object:alter{parts={3, 'string', 4, 'scalar'}} + + _index:update({_priv.id, _priv.index.primary.id}, + {{'=', 'parts', {{1, 'unsigned'}, {2, 'string'}, {3, 'scalar'}}}}) + _index:update({_vpriv.id, _vpriv.index.primary.id}, + {{'=', 'parts', {{1, 'unsigned'}, {2, 'string'}, {3, 'scalar'}}}}) + _index:update({_priv.id, _priv.index.object.id}, + {{'=', 'parts', {{2, 'string'}, {3, 'scalar'}}}}) + _index:update({_vpriv.id, _priv.index.object.id}, + {{'=', 'parts', {{2, 'string'}, {3, 'scalar'}}}}) end local function create_vinyl_deferred_delete_space() @@ -1079,8 +1085,9 @@ local function upgrade_func_to_2_2_1() format[18] = {name='created', type='string'} format[19] = {name='last_altered', type='string'} _func:format(format) - _func.index.name:alter({parts = {{'name', 'string', - collation = 'unicode_ci'}}}) + box.space._index:update( + {_func.id, _func.index.name.id}, + {{'=', 'parts', {{field = 2, type = 'string', collation = 2}}}}) end local function create_func_index() @@ -1146,7 +1153,8 @@ end local function drop_func_collation() local _func = box.space[box.schema.FUNC_ID] - _func.index.name:alter({parts = {{'name', 'string'}}}) + box.space._index:update({_func.id, _func.index.name.id}, + {{'=', 'parts', {{2, 'string'}}}}) end local function create_session_settings_space() @@ -1306,7 +1314,11 @@ local function schema_needs_upgrade() local schema_version, schema_version_str = get_version() if schema_version ~= nil and handlers[#handlers].version > schema_version then - return true, schema_version_str + local msg = string.format( + 'Your schema version is %s while Tarantool %s requires a more'.. + ' recent schema version. Please, consider using box.'.. + 'schema.upgrade().', schema_version_str, box.info.version) + return true, msg end return false end diff --git a/test/box-luatest/gh_7149_forbid_ddl_until_box_schema_upgrade_test.lua b/test/box-luatest/gh_7149_forbid_ddl_until_box_schema_upgrade_test.lua index 6956ce6a9261d5cdd4b52f6de02c1188ac76975c..b9bab98413e1a59a035d2df3197ab241611b5df7 100644 --- a/test/box-luatest/gh_7149_forbid_ddl_until_box_schema_upgrade_test.lua +++ b/test/box-luatest/gh_7149_forbid_ddl_until_box_schema_upgrade_test.lua @@ -21,3 +21,62 @@ g.test_schema_access = function() box.schema.upgrade() end) end + +g.before_test('test_ddl_ops', function() + -- Recover from Tarantool 1.10 snapshot + local data_dir = 'test/box-luatest/upgrade/1.10' + -- Disable automatic schema upgrade + local box_cfg = {read_only = true} + g.server = server:new{alias = 'master', + datadir = data_dir, + box_cfg = box_cfg} + g.server:start() +end) + +g.test_ddl_ops = function() + g.server:exec(function() + local t = require('luatest') + local error_msg = "DDL operations are not allowed: " .. + "Your schema version is 1.10.0 while Tarantool " + + -- Note that automatic schema upgrade will not be performed + box.cfg{read_only = false} + + t.assert_error_msg_contains(error_msg, + function() box.schema.space.create('test') end) + t.assert_error_msg_contains(error_msg, + function() box.schema.space.alter(box.space.T1.id, {}) end) + t.assert_error_msg_contains(error_msg, + function() box.schema.space.drop(box.space.T1.id) end) + t.assert_error_msg_contains(error_msg, + function() box.schema.index.create(box.space.T1.id, 'name') end) + t.assert_error_msg_contains(error_msg, + function() box.schema.index.alter(box.space.T1.id, 0, {}) end) + t.assert_error_msg_contains(error_msg, + function() box.schema.index.drop(box.space.T1.id, 0) end) + t.assert_error_msg_contains(error_msg, + function() box.schema.sequence.create('test') end) + t.assert_error_msg_contains(error_msg, + function() box.schema.sequence.alter('test', {}) end) + t.assert_error_msg_contains(error_msg, + function() box.schema.sequence.drop('test') end) + t.assert_error_msg_contains(error_msg, + function() box.schema.func.create('test') end) + t.assert_error_msg_contains(error_msg, + function() box.schema.func.drop('test') end) + + box.schema.upgrade() + + box.schema.space.create('test') + box.schema.space.alter(box.space.test.id, {}) + box.schema.space.drop(box.space.test.id) + box.schema.index.create(box.space.T1.id, 'name') + box.schema.index.alter(box.space.T1.id, 1, {}) + box.schema.index.drop(box.space.T1.id, 1) + box.schema.sequence.create('test') + box.schema.sequence.alter('test', {}) + box.schema.sequence.drop('test') + box.schema.func.create('test') + box.schema.func.drop('test') + end) +end diff --git a/test/box-luatest/upgrade/1.10/00000000000000000004.snap b/test/box-luatest/upgrade/1.10/00000000000000000004.snap new file mode 100644 index 0000000000000000000000000000000000000000..61ed92fc61744ecf481b114ace6ed68eec6d0128 Binary files /dev/null and b/test/box-luatest/upgrade/1.10/00000000000000000004.snap differ diff --git a/test/box/error.result b/test/box/error.result index a0633dbdae012bb31c768d2ae7478100c76df132..ba7bc0b5b8b23cd12bdfa091713060b248a24525 100644 --- a/test/box/error.result +++ b/test/box/error.result @@ -466,6 +466,7 @@ t; | 245: box.error.OLD_TERM | 246: box.error.INTERFERING_ELECTIONS | 247: box.error.ITERATOR_POSITION + | 248: box.error.DDL_NOT_ALLOWED | ... test_run:cmd("setopt delimiter ''");